Build a task manager with Next.js, Supabase, and Clerk
- Category
- Guides
- Published
Learn how to integrate Clerk with Supabase by building a task manager.
Supabase is an open-source backend-as-a-service platform that provides Postgres databases, authentication, instant APIs, realtime data subscriptions, and more to help developers quickly build scalable applications.
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. RLS works by validating database queries according to the restrictions defined in the RLS policies applied to the table. This guide will show you how to create RLS policies that restrict access to data based on the user's Clerk ID. This way, users can only access data that belongs to them. To set this up, you will:
- Create a function in Supabase to parse the Clerk user ID from the authentication token.
- Create a
user_id
column that defaults to the Clerk user's ID when new records are created. - Create policies to restrict what data can be read, inserted, updated, and deleted.
- Use the Clerk Supabase integration helper in your code to authenticate with Supabase and execute queries.
This guide will have you create a new table in your Supabase project, but once you've learned the concepts and the process, you can apply them to any existing table.
The source code for the final version can be found here.
Project setup
Clone the Clerk Next.js quickstart
To get started, clone the Clerk Next.js quickstart and install the dependencies:
If you're wondering how this project was created, check out the Next.js quickstart.
Set up a Clerk project
If you do not have a Clerk account, create one before proceeding. If you already have an account, create a new project for this guide.
Once you create an application, you'll be presented with the quickstarts. For this guide, follow the Next.js quickstart and only complete step 2, which instructs you to create the .env.local
file and populate it with the necessary environment variables. The remaining steps are already completed as part of the quickstart repository that you cloned in the previous step.
Run your project with the following command:
Visit your app's homepage at http://localhost:3000
. Sign up to create your first user and test everything works as expected.
Set up a Supabase project
If you do not have a Supabase account, create one before proceeding. If you already have an account, create a new project in the Supabase dashboard.
Create a SQL query that checks the user's Clerk ID
Now that you've set up your project, it's time to get to work.
Create a function named requesting_user_id()
that will parse the Clerk user ID from the authentication token. This function will be used to set the default value of user_id
in a table and in the RLS policies to ensure the user can only access their data.
- In the sidebar of your Supabase dashboard, navigate to SQL Editor. This is where you will run all your SQL queries for the rest of this guide. Paste the following into the editor:
- To execute the query and create the
requesting_user_id()
function, select Run. The results will be displayed in the Results tab, and should say "Success. No rows returned". Throughout this guide, keep an eye on this tab when running queries as it will display any errors that occur.
Create a table and enable RLS on it
Next, you'll create a "tasks"
table and enable RLS on that table. The "tasks"
table will also contain a user_id
column that will use the requesting_user_id()
function you just created as its default value. This column will be used in the RLS policies to only return or modify records scoped to the user's account.
To create the "tasks"
table and enable RLS on it, you'll run the following two queries. The first query creates the table and the second query enables RLS on the table.
Paste the following in the SQL Editor and select Run:
If you want to see the table you just created, in the sidebar, select Table Editor and select the "tasks"
table. You should see three empty columns: id
, name
, and user_id
.
Create ID-based RLS policies
Now, you need to create RLS policies that permit users to read, insert, update, and delete content associated with their user IDs only.
Paste the following in the SQL Editor and select Run:
Get your Supabase JWT secret key
Now that your table is set up and ready to be populated with data, it's time to set up your Clerk application and get Supabase integrated.
Get your Supabase JWT secret key:
- In the sidebar, navigate to Project Settings > API.
- Under the JWT Settings section, save the value in the JWT Secret field somewhere secure. This value will be used in the next step.
Create a Supabase JWT template
When sending requests to Supabase, Supabase needs to verify that the user is who they say they are. Because you're using Clerk to authenticate your users, you need Clerk to tell Supabase who the user is. This is where JWTs come in. For each authenticated user, Clerk issues JWTs that contain information about the user, like their ID. You're going to create a custom template for a JWT that Supabase can use.
To create a JWT template for Supabase:
- Navigate to the Clerk Dashboard.
- In the navigation sidebar, select JWT Templates.
- 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.
- You can leave all other fields at their default settings or customize them to your needs. See the JWT template guide to learn more about these settings.
- Select Save from the notification bubble to complete setup.
- The value of the Name field will be required when using the template in your code. For this tutorial, name it
Install the Supabase client library
Add the Supabase client library to your project by running the following command in your terminal:
Set up your environment variables
Add the Supabase project URL and key to your .env.local
file.
- In the sidebar of the Supabase dashboard, select Settings > API.
- Add the Project URL to your
.env.local
file asSUPABASE_URL
. - In the Project API keys section, add the value beside
anon
public
to your.env.local
file asSUPABASE_KEY
.
Fetch Supabase data in your code
Let's get to coding!
You want to load a list of tasks for the user and allow the user to add new tasks, mark tasks as complete, and delete tasks. You can do this either on the client-side or server-side.
Client-side rendering (CSR)
The following example demonstrates how to fetch data from Supabase in a client-side rendered page.
The createClerkSupabaseClient()
function uses Supabase's createClient()
method to initialize a new Supabase client, but modifies it to inject the Clerk token you created with the Supabase JWT template into the request headers sent to Supabase. The requesting_user_id()
function that was created in the Supabase dashboard will parse the user ID from the Clerk token and use it when querying data from the tasks
table.
Paste the following code into the app/page.tsx
file:
Server-side rendering (SSR)
The following example demonstrates how to fetch data from Supabase in a server-side rendered page. It requires creating multiple files as you will use Server Actions to handle adding, deleting, and updating tasks.
The createClerkSupabaseClientSsr()
function uses Supabase's createClient()
method to initialize a new Supabase client, but modifies it to inject the Clerk token you created with the Supabase JWT template into the request headers sent to Supabase. The requesting_user_id()
function that was created in the Supabase dashboard will parse the user ID from the Clerk token and use it when querying data from the tasks
table.
It is stored in a separate file so that it can be reused in multiple places, such as in both your page.tsx
and your Server Action file.
Create the src/app/ssr/client.ts
file and paste the following code into it:
Create the src/app/ssr/page.tsx
file and paste the following code into it:
Create a src/app/ssr/actions.ts
file which will include Server Actions for adding, deleting, and updating tasks. Paste the following code into that file:
Your form for adding tasks will use the addTask()
Server Action that you created in the previous file. The form is in a separate file than your page.tsx
file because it must be a client component. Create the src/app/ssr/AddTaskForm.tsx
file and paste the following code into it:
Create the src/app/ssr/TaskRow.tsx
file which will display the tasks in your list. It's a separate file from your page.tsx
because it uses the useRouter()
hook, so it must be a client component. Paste the following code into that file:
Sign in and test viewing, creating, updating, and deleting tasks. Sign out and sign in as a different user, and repeat.
If you have the same tasks across multiple accounts, double-check that RLS is enabled, or that the RLS policies were properly created. Check the table in the Supabase dashboard. You should see all the tasks between both users but with differing values in the user_id
column.
Ready to get started?
Sign up today