Integrating Supabase with Clerk gives you the benefits of using a Supabase database while leveraging Clerk's authentication, prebuilt components, and webhooks. To get the most out of Supabase with Clerk, you must implement custom Row Level Security (RLS) policies.
To show users content scoped to their account, you must create RLS policies that check the user's Clerk ID. This requires storing Clerk user IDs on the relevant tables. In this guide we will use a column named user_id, but you can use any name you would like.
In the sidebar of your Supabase dashboard, navigate to Database > Tables. From here, you can add the user ID column to the table you want to use.
Name the column user_id.
This column's data type must be text.
In the Default Value field, add (requesting_user_id()). This will make it default to the return value of the custom function you'll define in the next step. Doing this enables you to make each requesting user's ID available to Supabase from the request headers.
Note
This step is required because Supabase's auth.uuid() function, which normally grants access to the user ID in RLS policies, is not compatible with Clerk's user IDs.
Create a requesting_user_id() function, which will get the Clerk user ID of the requesting user from the request headers. This will allow you to access the user ID in your RLS policies.
In the sidebar of your Supabase dashboard, navigate to SQL Editor, then select New query. Paste the following into the editor:
Select Run to execute the query and create the requesting_user_id function.
Create RLS policies that allow users to modify and read content associated with their user IDs. This example will use an Addresses table, but you can replace Addresses with whatever table you're using.
Create an RLS policy for inserting content:
In your Supabase dashboard, in the sidebar, navigate to Authentication > Policies. Under the name of the table you want users to have access to, select New Policy.
If you're using the policy editor, paste the following snippet, replacing address and "Addresses" with whatever you want:
If you're using the policy creator instead of the editor:
Name the policy whatever you want.
For Allowed operation, select INSERT.
For Target roles, select authenticated.
For the USING expression, paste the following:
Create another RLS policy to allow users to read content from the same table they can modify. Follow the same instructions as the previous step, but the Allowed operation must be SELECT instead of INSERT.
If you're using the editor, copy the same snippet from the previous step, replacing FOR INSERT with FOR SELECT.
To give users access to your data, Supabase's API requires an authentication token. Your Clerk project can generate these authentication tokens, but it needs your Supabase project's JWT secret key first.
To find the JWT secret key:
In the Supabase dashboard, select your project.
In the sidebar, select Settings > API. Copy the value in the JWT Secret field.
Clerk's JWT templates allow you to generate a new valid Supabase authentication token for each signed in user. These tokens allow authenticated users to access your data with Supabase's API.
To create a JWT template for Supabase:
Open your project in the Clerk Dashboard and navigate to the JWT Templates page in the sidebar.
Select the New template button, then select Supabase from the list of options.
Configure your template:
The value of the Name field will be required when using the template in your code. For this tutorial, name it supabase.
Signing algorithm will be HS256 by default. This algorithm is required to use JWTs with Supabase. Learn more in their docs.
Under Signing key, add the value of your Supabase JWT secret key from the previous step.
Leave all other fields at their default settings unless you want to customize them. See Clerk's JWT template docs to learn what each of them do.
To use Clerk with Supabase in your code, first install the necessary SDKs by running the following terminal command in the root directory of your project:
Next.js
React
npm
yarn
pnpm
npm
yarn
pnpm
Then, set up your environment variables:
If you don't have a .env.local file in the root directory of your Next.js project, create one now.
Find your Clerk publishable key and secret key. If you're signed into Clerk, the .env.local snippet below will contain your keys. Otherwise:
Navigate to your Clerk Dashboard.
Select your application, then select API Keys in the sidebar menu.
You can copy your keys from the Quick Copy section.
Add your keys to your .env.local file.
Find your Supabase credentials:
Go to your Supabase dashboard. In the sidebar, select Settings > API.
Copy the Project URL and add it to your .env.local file.
Copy the value beside anonpublic in the Project API Keys section and add it to your .env.local file.
The following steps will show you how to access content from your Supabase tables based on the user's ID. It assumes you have a table named "Addresses" with a content field, but you can adapt this code for any use case.
Client component
Server component
Create a component and define a createClerkSupabaseClient method. This method returns a client that connects to Supabase with an authentication token from your Clerk JWT template:
Next.js App Router
Next.js Pages Router
React
Next, define a component with methods for listing addresses from and sending addresses to your database:
Next.js App Router
Next.js Pages Router
React
Finally, edit your component to return a basic UI that allows you to list all your addresses and send new ones:
Next.js App Router
Next.js Pages Router
React
The final result should be similar to this:
Next.js App Router
Next.js Pages Router
React
Try out your application. When you visit the page with your component, you'll be required to sign in. Try creating and fetching content.
To create a Supabase client in a Server component, you must first install the Supabase SSR package:
npm
yarn
pnpm
Create a component and define a createClerkSupabaseClient method. This method returns a client that connects to Supabase with an authentication token from your Clerk JWT template:
Next, define a component with methods for accessing a user's addresses from your database:
Finally, edit your component to return a basic UI that allows you to list all your addresses:
The final result should be similar to this:
Try out your application. When you visit the page with your component, you'll be required to sign in. Try creating and fetching content.