Integrating Clerk, Prisma, and Supabase into Next.js for User Session Management

Leandro Ercoli
4 min readMay 14, 2024

--

Setting up user sessions isn’t just about signing users in; it’s about integrating several systems to work together, handling security, managing user data, and much more. It’s a lot, and often, it’s exactly these kinds of necessary but tricky tasks that slow down our progress, turning an exciting project into a series of frustrating technical challenges.

We can turn those daunting tasks into manageable steps by combining different tools, such as Clerk for authentication, Prisma for object-relational mapping, and Supabase as a backend service.

By the way, everything I discuss in this article comes with my free Next.js Boilerplate Kit, SaasterKit. I’ve included documentation and a functional dashboard example, so you can skip the guesswork and dive right into seeing how it all clicks together.

Setting the Stage for User Sessions: Integrating Clerk into Next.js

We start by wrapping our application with ClerkProvider, and use ClerkLoading and ClerkLoaded components to manage and observe authentication state in Clerk. The SignInButton and UserButton components will render a sign in interface and a user profile dropdown, respectively. The SignedIn and SignedOut components help in conditionally rendering UI based on the user’s authentication state.

// src/app/layout.tsx

import {
ClerkLoaded,
ClerkLoading,
ClerkProvider,
SignInButton,
SignedIn,
SignedOut,
UserButton,
} from '@clerk/nextjs';

import '../globals.css';
import Loading from './loading';

export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en" suppressHydrationWarning>
<ClerkProvider>
<body className="w-screen min-h-screen flex bg-background">
<ClerkLoading>
<Loading />
</ClerkLoading>
<ClerkLoaded>
<header
className="sticky top-0 w-full h-16 flex justify-between items-center gap-4 border-b px-6"
>
<span>My App</span>
<SignedIn>
<UserButton />
</SignedIn>
<SignedOut>
<SignInButton />
</SignedOut>
</header>
{children}
</ClerkLoaded>
</body>
</ClerkProvider>
</html>
);
}

Bridging the Gap: Integrating Clerk with Next.js via Webhooks

Once we’ve got Clerk up and running, we have to make sure that whatever happens in Clerk matches what’s in our database. Whenever someone signs up or deletes their account using Clerk, we also have to create or delete the user from our database.

We set up a webhook from Clerk’s dashboard to notify our app instantly whenever there’s a new session event, firing an HTTP POST request to the specified endpoint in our Next.js API.

Heads up! To test webhook functionality locally, you will have to use tools like localtunnel to expose your localhost port to the internet. You can find more details on how to do this on the official docs.

Set up a Webhook in Clerk to send events to our application

Making Data Persistence Simple: Integrating Prisma ORM and Supabase

With our Next.js application now receiving user events in real-time, the next step is to persist these changes in our database. We will use Prisma to manage our app’s data with ease. It is an open-source Object-Relational Mapping (ORM) tool that provides a type-safe API to interact with our preferred database.

Supabase has been my go-to backend provider for a while now. It’s robust, performant, well-documented and has a very generous free tier that’s perfect for building MVPs and validating ideas quickly.

// schema.prisma 

generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = env("SUPABASE_DATABASE_URL")
directUrl = env("SUPABASE_DIRECT_URL")
}

// User - Associated with a clerk user
model User {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
clerkUserId String? @unique
}

Now, we are ready to set up an API route in our Next.js application to be called by Clerk’s webhook whenever there is a user session update. In the new route handler, we use the upsert function provided by Prisma, which updates the existing user data if found, or inserts new data if it does not exist in the database. On the other hand, we use the delete function to delete the user from our database when the account is deleted through Clerk’s service.

// src/app/api/webhooks/clerk

import { prisma } from '@/libs/database';
import type { WebhookEvent } from '@clerk/nextjs/server';
import { NextResponse } from 'next/server';

// Clerk Webhook: create or delete a user in the database by Clerk ID
export async function POST(req: Request) {
try {
// Parse the Clerk Webhook event
const evt = (await req.json()) as WebhookEvent;

const { id: clerkUserId } = evt.data;
if (!clerkUserId)
return NextResponse.json(
{ error: 'No user ID provided' },
{ status: 400 },
);

// Create or delete a user in the database based on the Clerk Webhook event
let user = null;
switch (evt.type) {
case 'user.created': {
user = await prisma.user.upsert({
where: {
clerkUserId,
},
update: {
clerkUserId,
},
create: {
clerkUserId,
},
});
break;
}
case 'user.deleted': {
user = await prisma.user.delete({
where: {
clerkUserId,
},
});
break;
}
default:
break;
}

return NextResponse.json({ user });
} catch (error) {
return NextResponse.json({ error }, { status: 500 });
}
}

The End Result

With all these elements in place — Clerk ensuring secure, easy-to-manage user sessions, Prisma providing a smooth database experience with simple ORM, and Supabase standing strong as the backend — our Next.js project has now a robust, effective user session and profile management.

We can augment the user’s session with relevant data for our application, like profile information, account preferences, or application data.

If you think this might be a good solution, here’s a complete free boilerplate kit that you can use as a starting point for your next project. It’s fully integrated with authentication and database services and includes preconfigured features for emailing, error tracking, i18n, a landing page template and a functional dashboard example.

Thank you for reading!

SaasterKit: A Complete Boilerplate Kit for Next.js Projects

--

--

Leandro Ercoli

Computer Engineer — Full-stack web developer and multi-platform mobile developer