Table of Contents
- Introduction
 - What is Clerk
 - What is MongoDB
 - So What makes it different from SQL Databases
 - How to Setup Clerk in a Next.js Project
 - What to do Next ?
 - It’s time to start with MongoDB
 - Sync Clerk DB with MongoDB using Webhooks
 - Conclusion
 
Introduction
In this guide, we’re going to explore how to integrate Clerk authentication with a Next.js application, connect it to a MongoDB database, and handle user events using webhooks.
Prerequisites:
- Next.js: Basic knowledge of creating and running apps
 - TypeScript: Familiarity with TypeScript syntax
 - Backend concepts: Database and HTTP basics
 
What is Clerk
Clerk is a user management platform that provides authentication UIs, APIs, and dashboards. It supports multifactor auth, session management, and user profiles out of the box.
What is MongoDB
MongoDB is a NoSQL database that stores data in JSON-like documents. It supports structured, semi-structured, and unstructured data — offering flexibility and scalability.
So What makes it different from SQL Databases
SQL databases store data in tables with fixed schemas. NoSQL like MongoDB allows flexible schemas, ideal for evolving or mixed data types.
How to Setup Clerk in a Next.js Project
- Install Clerk:
 
npm install @clerk/nextjs- Set environment variables in 
.env.local: 
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEYCLERK_SECRET_KEY=YOUR_SECRET_KEY- Add Clerk middleware:
 
import { authMiddleware } from "@clerk/nextjs";
export default authMiddleware({  publicRoutes: ["/"],});
export const config = {  matcher: [    "/((?!.+\\.[\\w]+$|_next).*)",    "/(api|trpc)(.*)",  ],};- Wrap your layout in 
<ClerkProvider>: 
<ClerkProvider  appearance={{    elements: {      formButtonPrimary: "primary-gradient",      footerActionLink: "primary-text-gradient hover:text-primary",    },  }}>  <ThemeProvider>{children}</ThemeProvider></ClerkProvider>What to do Next ?
➡️ Read Next.js: Build custom sign-in pages with Clerk
It’s time to start with MongoDB
- Install dependencies:
 
npm install mongodb mongoose- 
Create a MongoDB Atlas cluster and get your connection string.
 - 
Add it to
.env.local: 
MONGODB_URL=mongodb+srv://<username>:<password>@cluster0.mongodb.net/ProjectName- Create a 
mongoose.tsconnection utility: 
import mongoose from "mongoose";
let isConnected = false;
export const connectToDatabase = async () => {  mongoose.set("strictQuery", true);  if (!process.env.MONGODB_URL) return console.log("🔴 MISSING MONGODB_URL");  if (isConnected) return console.log("🟢 Already connected to MONGODB");
  try {    await mongoose.connect(process.env.MONGODB_URL, { dbName: "DBName" });    isConnected = true;    console.log("🟢 Connected to MONGODB");  } catch (error) {    console.log("🔴 Error connecting to MONGODB", error);  }};Sync Clerk DB with MongoDB using Webhooks
- 
Deploy your app on Vercel
 - 
In Clerk Dashboard → Webhooks → Add Endpoint:
 
https://your-website.com/api/webhook- Add to 
.env.local: 
NEXT_CLERK_WEBHOOK_SECRET=your_webhook_secret- Install svix:
 
npm install svix- Create 
api/webhook/routes.ts: 
import { Webhook } from "svix";import { headers } from "next/headers";import { NextResponse } from "next/server";import { createUser, updateUser, deleteUser } from "@/lib/actions/user.actions";
export async function POST(req: Request) {  const WEBHOOK_SECRET = process.env.NEXT_CLERK_WEBHOOK_SECRET;  if (!WEBHOOK_SECRET) throw new Error("Missing Clerk webhook secret.");
  const headerPayload = headers();  const svix_id = headerPayload.get("svix-id");  const svix_timestamp = headerPayload.get("svix-timestamp");  const svix_signature = headerPayload.get("svix-signature");
  if (!svix_id || !svix_timestamp || !svix_signature) {    return new Response("Missing headers", { status: 400 });  }
  const payload = await req.json();  const body = JSON.stringify(payload);  const wh = new Webhook(WEBHOOK_SECRET);
  let evt;  try {    evt = wh.verify(body, {      "svix-id": svix_id,      "svix-timestamp": svix_timestamp,      "svix-signature": svix_signature,    });  } catch (err) {    console.error("Verification failed", err);    return new Response("Verification error", { status: 400 });  }
  const eventType = evt.type;
  if (eventType === "user.created") {    const { id, email_addresses, image_url, username, first_name, last_name } = evt.data;    const mongoUser = await createUser({      clerkId: id,      name: `${first_name} ${last_name ?? ""}`,      email: email_addresses[0].email_address,      username: username!,      picture: image_url,    });    return NextResponse.json({ message: "User created", user: mongoUser });  }
  if (eventType === "user.updated") {    const { id, email_addresses, image_url, username, first_name, last_name } = evt.data;    const mongoUser = await updateUser({      clerkId: id,      updateData: {        name: `${first_name} ${last_name ?? ""}`,        email: email_addresses[0].email_address,        username: username!,        picture: image_url,      },      path: `/profile/${id}`,    });    return NextResponse.json({ message: "User updated", user: mongoUser });  }
  if (eventType === "user.deleted") {    const { id } = evt.data;    const deletedUser = await deleteUser({ clerkId: id! });    return NextResponse.json({ message: "User deleted", user: deletedUser });  }
  return new Response("", { status: 200 });}Conclusion
You now have:
- ✅ Clerk authentication in Next.js
 - ✅ MongoDB set up with Mongoose
 - ✅ Webhooks syncing data between Clerk and MongoDB
 
You can test webhooks using the Send Test button from Clerk Dashboard. If anything breaks, check your Vercel logs!