621 words
3 minutes
Next.js Auth with Clerk and MongoDB
2024-05-08

Table of Contents#


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#

  1. Install Clerk:
Terminal window
npm install @clerk/nextjs
  1. Set environment variables in .env.local:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY
CLERK_SECRET_KEY=YOUR_SECRET_KEY
  1. Add Clerk middleware:
import { authMiddleware } from "@clerk/nextjs";
export default authMiddleware({
publicRoutes: ["/"],
});
export const config = {
matcher: [
"/((?!.+\\.[\\w]+$|_next).*)",
"/(api|trpc)(.*)",
],
};
  1. 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#

  1. Install dependencies:
Terminal window
npm install mongodb mongoose
  1. Create a MongoDB Atlas cluster and get your connection string.

  2. Add it to .env.local:

MONGODB_URL=mongodb+srv://<username>:<password>@cluster0.mongodb.net/ProjectName
  1. Create a mongoose.ts connection 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#

  1. Deploy your app on Vercel

  2. In Clerk Dashboard → Webhooks → Add Endpoint:

https://your-website.com/api/webhook
  1. Add to .env.local:
NEXT_CLERK_WEBHOOK_SECRET=your_webhook_secret
  1. Install svix:
Terminal window
npm install svix
  1. 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!

Next.js Auth with Clerk and MongoDB
https://chouaibdev.vercel.app/posts/nextjs-clerk/
Author
Chouaib Djerdi
Published at
2024-05-08
License
CC BY-NC-SA 4.0