DEV Community

Cover image for Make User Management easier with Clerk + Supabase 🔥🔥
Shivam Katare
Shivam Katare

Posted on

Make User Management easier with Clerk + Supabase 🔥🔥

Handling user authentication and connecting to a database can be tough, especially with modern web apps. Clerk and Supabase are two great tools that make this process easier, providing smooth user management and scalable database options. In this article, we’ll learn, how to connect Clerk's authentication system with Supabase to build a strong and secure user management process.

So, without wasting a bit, let’s get started.

lets go

Initial Setup

The first thing that, we'll do is set up our Next.js project and install the necessary dependencies.

Install Dependencies

Run the following command to install Next.js on your machine.

npx create-next-app@latest 

Enter fullscreen mode Exit fullscreen mode

On installation, you'll see the following prompts:


What is your project named? my-app
Would you like to use TypeScript? No / Yes   => I picked no
Would you like to use ESLint? No / Yes => I picked yes
Would you like to use Tailwind CSS? No / Yes => I picked yes
Would you like your code inside a `src/` directory? No / Yes  => I picked no
Would you like to use App Router? (recommended) No / Yes  => I picked yes
Would you like to use Turbopack for `next dev`?  No / Yes  => I picked no
Would you like to customize the import alias (`@/*` by default)? No / Yes => I picked no
What import alias would you like configured? @/* => I picked @

Enter fullscreen mode Exit fullscreen mode

Our next js has been installed, now let’s install clerk and supabase. Run this command to install clerk👇

npm i @clerk/nextjs

Enter fullscreen mode Exit fullscreen mode

and this one to install supabase👇

npm i @supabase/supabase-js
Enter fullscreen mode Exit fullscreen mode

We've installed the necessary dependencies, so now let's set up the Supabase and Clerk dashboards to ensure they work together.

Setup Clerk Dashboard

Click here to sign up for Clerk or sign in to your existing Clerk account.

If you've just created an account for the first time, you'll go straight to the authentication setup form.

clerk form

If not, you'll be sent to your Clerk Dashboard. To make a new app, click on the Create application card to access the authentication setup form. Fill in all the details and also select social auth providers like Google, Github, Facebook, etc. whatever you want.

clerk setup

It will create your new clerk application on which you can start adding your users with their auth flow. Your dashboard will look like this. 👇

Image description

That’s it, clerk setup is done now let’s set up Supabase.

Setup Supabase Dashboard

Go to this link to sign in to your Supabase dashboard.

supabase

Now, go to this link to create a new Supabase project. Once your project is created, it should be visible in your dashboard like this.

Image description

Our supabase dashboard is also set. Now, let’s see the integration part.

Integrate Clerk

We've installed all our dependencies and set up both dashboards. Now, we'll integrate Clerk and Supabase into our app, one by one. We'll start with Clerk first, then move on to Supabase.

Before moving forward, let's look at the folder structure. I have added a few new files and folders that we will use later in this article.

Image description

Set Clerk Provider

The first thing we need to do is wrap our app in a <ClerkProvider /> so it can manage our auth state from the top of the hierarchy. The <ClerkProvider> component wraps your app to provide active session and user context to Clerk's hooks and other components.

Head over to your layout.js file, and wrap your main component into <ClerkProvider/> 👇

export default function RootLayout({ children }) {
  return (
    <ClerkProvider touchSession={false} >
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} 
          antialiased`}
      >
        {children}
      </body>
    </html>
    </ClerkProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

ClerkProvider has several props. As you can see, I’ve added one prop called touchSession. By default, it's true, but you can set it to false. When it's false, it won't check the user session on every click. Otherwise, it will call an API each time to see if the user’s session is active. For a full list of ClerkProvider props, you can check it here.

Now, let's add sign-in and sign-up buttons. Clerk provides its own UI and components to make this process easier, like this 👇

import localFont from 'next/font/local';
import './globals.css';
import {
  ClerkProvider,
  SignedIn,
  SignedOut,
  SignInButton,
  UserButton,
} from '@clerk/nextjs';

// rest of your code

export default function RootLayout({ children }) {
  return (
    <ClerkProvider touchSession={false}>
      <html lang="en">
        <body
          className={`${geistSans.variable} ${geistMono.variable} antialiased`}
        >
         <header>  // here you can use styling to style it a/c to your design
          <SignedOut>
            <SignInButton />
          </SignedOut>
          <SignedIn>
            <UserButton />
          </SignedIn>
        </header>
          {children}
        </body>
      </html>
    </ClerkProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

That’s it, now let’s set up env variables.

Setup clerk env

In the Clerk Dashboard, navigate to the Configure > API Keys page.

In the Quick Copy section, copy your Clerk publishable and secret key.

Image description

Paste your keys into your .env.local file.

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=clerk_publishable_key
CLERK_SECRET_KEY=thiswillbeyoursecretkey

Enter fullscreen mode Exit fullscreen mode

Middleware Setup

The last important step is to set up the middleware.

Middleware helps us protect our routes. For example, if we have three routes named login, dashboard, and about, and we don't want users to access the dashboard and about without logging in, auth middleware can help us with this.

For Clerk, we'll set up our middleware based on our route structure. Create a middleware.js file in your root folder and add this code 👇

import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
import { NextResponse } from 'next/server'

const isProtectedRoute = createRouteMatcher(['/user(.*)'])
const isPublicRoute = createRouteMatcher(['/'])

export default clerkMiddleware(async (auth, req) => {
  const { userId } = await auth()

  if (userId && req.nextUrl.pathname === '/') {
    const userUrl = new URL('/user', req.url)
    return NextResponse.redirect(userUrl)
  }

  if (isProtetedRoute(req)) {
    await auth.protect()
  }
})
Enter fullscreen mode Exit fullscreen mode

Here, we're indicating that /user routes are protected. So, if any user tries to access this route without logging in, they will first be redirected to the / route to sign in or sign up.

The Clerk setup is complete. You can test your sign-in and sign-up process, and all your users will be visible on your Clerk dashboard.

Integrate Supabase

Now that we have our authentication flow set up, it's time to save our logged-in users in our Supabase database. When a user signs up in your app using the Clerk provider, Clerk will generate an authentication token and send it to Supabase using the Supabase project's JWT secret key.

You'll provide the Supabase JWT secret key to Clerk, and Clerk will share the auth token with Supabase. With this connection, you'll be able to save your user's unique user_id. After that, we can perform any CRUD operations.

Create Function

First, we'll create a function in our Supabase to request the user ID from Clerk. Then, we'll pass our Supabase JWT to Clerk, to connect them.

In the sidebar of your Supabase dashboard, go to Database > Functions.

supabase function

Click on the Create New Function button 👇

functions

Enter function name

Return type text

and add this definition

SELECT NULLIF(
    current_setting('request.jwt.claims', true)::json->>'sub',
    ''
)::text;
Enter fullscreen mode Exit fullscreen mode

supabase

Scroll a bit, and turn on the advanced settings option

select language sql

Click Confirm to save it.

save supabase

Next, create a user_id column in the table you want to secure. This column will be used in the RLS policies to only return or modify records related to the user's account. It will use the requesting_user_id_from_clerk() function we just created as its default value.

Go to the sidebar on the left and select Table Editor.
Select the table you wish to secure.
In the table, select the + column to add a new column.

Image description

Set the Name to user_id.
Set Type to text.
Set Default Value to requesting_user_id_from_clerk().

Select Save to create the column.

save functions

Make sure your RLS policy is enabled for that particular table.

In the top bar above the table, you can see the options to Enable RLS, Disable RLS, or Add New RLS Policy. Currently, I have one auth policy, so it shows 1 here.

rls

Here you can see all your policies, click on the Create New Policy button

rls

In this way, you can create an RLS policy for insert, delete, view, and update actions. As you can see, this RLS policy is for insert, and we are targeting public roles (point 4 in the screenshot).

adding rls

Similarly, you can do the same for SELECT authenticated roles. like this 👇

rls

Same for the UPDATE. This time, without check expression.👇

rls

To learn more about RLS Policies, you can check out this guide of supabase.

Get JWT Token

Supabase's API requires an authentication token to allow users to access your data. Your Clerk project can generate these tokens, but it first needs the JWT secret key from your Supabase project. As we discussed earlier.

To get JWT Secret:

  • Go to Settings from the sidebar
  • Then click on the API Section.
  • Then, click on Reveal and copy the JWT Secret.

get jwt

Now that we've finished the Supabase part, we'll create a Supabase JWT template on Clerk and integrate the JWT secret there.

Clerk JWT Template Setup

Go to your clerk dashboard, and head over to Clerk JWT Template. Click on the new template option.

clerk jwt

Select Supabase.

select supabase

Fill out 2 details here:

  1. Name: If you have multiple Clerk projects and multiple JWT Templates for each project, make sure to give this name a slight difference from the others.
  2. Then, paste your Supabase JWT Key into the signing key box below.

Now click save.

Image description

Great, now we have done almost everything important. Two things are left: setting up the environment for Supabase and integrating the Supabase client into our application. Then, we'll be ready to go.

Supabase Client Setup

Create a new folder in your root directory named lib and inside that create a file supabaseClient.js

Image description

And, add this code to it

import { createClient } from '@supabase/supabase-js';
// Function to create a Supabase client with Clerk authentication
export function createClerkSupabaseClient(session) {  
  return createClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL,
    process.env.NEXT_PUBLIC_SUPABASE_KEY,
    {
      global: {
        fetch: async (url, options = {}) => {
          const clerkToken = await session?.getToken({
            template: "enter-your-jwt-clerk-template-name",
          });
          const headers = new Headers(options?.headers);
          headers.set("Authorization", `Bearer ${clerkToken}`);
          return fetch(url, { ...options, headers });
        },
      },
    }
  );
}
Enter fullscreen mode Exit fullscreen mode

Here, we have two envs, so we'll add these to our .env.local file. Go to Settings > API and copy the project URL and anon key.

Image description

and paste them here in .env.local with clerk’s env variables.

NEXT_PUBLIC_SUPABASE_URL=""
NEXT_PUBLIC_SUPABASE_KEY=""
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=""
CLERK_SECRET_KEY=""
Enter fullscreen mode Exit fullscreen mode

And, that’s it. We are good to go. Now you can run the server using yarn, npm, or pnpm. Whatever you choose. I am using npm.👇

npm run dev
Enter fullscreen mode Exit fullscreen mode

Once the server starts, you can click on the Sign In button to log in. Without signing in, you won't be able to access /user/dashboard and /user/about, as we have set this restriction in our middleware.js file.

output

Conclusion

That’s it for today. In this article, we learn about how we can integrate Clerk's authentication system with Supabase in a Next.js app to build a secure user management process. We covered setting up the Next.js environment, installing necessary dependencies, configuring the Clerk and Supabase dashboards, implementing authentication, and authorization using middleware.

I hope you learn something from this blog, make sure to share it with your friends and community, and if you learn while reading it, make sure to give it a like and leave a comment. I write blogs and share content on JavaScript, TypeScript, Open Source, and other web development-related topics. Feel free to follow me on my socials. I'll see you in the next one.

BYE 👋

Useful Links:

Clerk get started guide: https://clerk.com/docs/quickstarts/setup-clerk

Supabase get started guide: https://supabase.com/docs/guides/getting-started

Clerk + Supabase Guide: https://clerk.com/docs/integrations/databases/supabase

Supabase + Clerk Guide: https://supabase.com/partners/integrations/clerk

Top comments (0)