January 26, 2024
O. Wolfson
This ariticle describes the process of building a Next.js 14 application with Clerk and Supabase. This app handles user authentication with Clerk and manages data with Supabase.
My Example app, Depoloyed at Vercel.
Clerk and Next.js Quickstart. Clerk Docs
Clerk and Supabase. Supabase Docs
Deploy to production. Clerk Docs
shnpx create-next-app@latest my-app
cd my-app
shnpm install @clerk/nextjs @supabase/supabase-js
Set up the environment for your Next.js project by integrating Clerk and Supabase. This involves obtaining the necessary API keys from both Clerk and Supabase and configuring them in your project.
Obtaining Clerk API Keys:
Obtaining Supabase API Keys:
Visit the Supabase Dashboard.
Sign in or create a new Supabase account.
Create a new project or select an existing one.
Once your project dashboard is open, go to the 'Settings' > 'API' section.
You will find two keys here: the anon
public key and the service_role
secret key. For most client-side operations, the anon
key is what you'll use.
You will also need Supabase's jwt secret key. To obtain this key, go to the 'Settings' > 'API'. You will find the jwt secret key here under the Config section.
In your Clerk application, use the lefthand menu to navigate to the JWT Templates page. Click on the button to create a new template based on Supabase. Name your template (I've named mine supabase, as in the example shown here in Supabase's Clerk Integration Docs) and click on the Create Template button. Enter your Supabase jwt secret key in the Signing Key input and apply changes. Once you have done this, you should be able to generate a token for your Supabase client.
For more information check out the .
add api keys to .env.local as follows:
textNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your-clerk-publishable-key CLERK_SECRET_KEY=your-clerk-secret-key NEXT_PUBLIC_SUPABASE_URL=your-supabase-url NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anonkey # add these values for the Clerk URLs: NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/ NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/
This environment setup ensures that your application can communicate securely with Clerk and Supabase services. Remember, these keys should be kept secure and not exposed in client-side code beyond what is necessary for the functionalities provided by Clerk and Supabase.
Integrating Clerk into your Next.js application involves setting up the ClerkProvider
in your main layout, creating a navigation bar with authentication controls, and setting up dedicated pages for sign-in and sign-up. Here's how to go about it:
Clerk Setup in layout.tsx
:
The ClerkProvider
component is used to wrap your application's root layout. This ensures that Clerk's authentication functionalities are accessible throughout your app.
Open or create a layout.tsx
file in your project.
Import ClerkProvider
from @clerk/nextjs
.
You'll also need to import the useRouter
hook from next/router
.
Wrap your layout component's content with ClerkProvider
, passing in the Clerk Frontend API key from your environment variables.
Example implementation:
tsximport type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { ClerkProvider } from "@clerk/nextjs";
import NavBar from "@/components/nav/nav-bar";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function () {
(
);
}
By following these steps, you'll have integrated Clerk into your Next.js application, providing a seamless authentication experience for your users. Remember to style and customize your NavBar
and authentication pages to match the look and feel of your application.
Integrating Supabase into your Next.js application involves initializing the Supabase client and configuring row-level security to manage data access. Here’s a guide on how to set this up:
Create a file named lib/supabase.ts
.
Import the createClient
method from @supabase/supabase-js
.
Initialize the Supabase client using the Supabase URL and ANON key from your environment variables.
Example:
typescriptimport { createClient } from "@supabase/supabase-js";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
Server-side setup is crucial for operations that should be securely executed on the server, like direct database manipulations.
Import Clerk's Server-Side Auth Module:
javascriptimport { auth } from "@clerk/nextjs/server";
Retrieve the Authentication Token:
javascriptconst { getToken } = auth();
const token = await getToken({ template: "supabase" });
Create the Supabase Client:
javascriptimport { createClient } from "@supabase/supabase-js";
const supabaseServerClient = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
{
global: {
headers: {
Authorization: `Bearer ${token}`,
},
},
}
);
Usage:
supabaseServerClient
for server-side database operations.The client-side setup is used for operations that require real-time user interaction, like form submissions.
Import Clerk's Client-Side Auth Hook:
javascriptimport { useAuth } from "@clerk/clerk-react";
Initialize the Supabase Client Inside a React Component:
javascriptimport { useState, useEffect } from "react";
import { createClient } from "@supabase/supabase-js";
function MyComponent() {
const [supabaseClient, setSupabaseClient] = useState(null);
const { getToken } = useAuth();
useEffect(() => {
const initializeSupabase = async () => {
const token = await getToken({ template: "supabase" });
const client = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
{
global: {
headers: {
Authorization: `Bearer ${token}`,
},
},
}
);
setSupabaseClient(client);
};
initializeSupabase();
}, []);
// ... rest of your component
}
:
By following these steps, you will successfully integrate Supabase into your Next.js application, complete with a standard client for general database operations and a Clerk-authenticated client for user-specific data access. Additionally, setting up row-level security ensures that your application's data access is secure and compliant with your access policies.
.env.local
Configuration:
After obtaining your API keys, the next step is to securely add them to your Next.js application using an environment file.
.env.local
in the root of your Next.js project (if it doesn't already exist).NavBar Component:
The NavBar
component serves as the main navigation header for your application, providing links and authentication controls available globally.
I recommend using Clerk's UserButton
component to handle authentication controls like sign-out and account management. Here I have added the UserButton to an AuthButton
component that also handles the case where the user is not authenticated and offers a link to the sign-in page. If the user is logged in the UserButton will be displayed.
tsximport React from "react";
import { UserButton } from "@clerk/nextjs";
import Link from "next/link";
import { auth } from "@clerk/nextjs/server";
function AuthButton() {
const { userId } = auth();
// console.log("user id:", userId);
return (
<div>
{userId ? (
<div className="border-2 rounded-full w-[35px] h-[35px]">
<UserButton afterSignOutUrl="/" />
</div>
) : (
<Link
href="/sign-in"
className="border-2 rounded-lg px-4 py-2 font-bold"
>
Log In
</Link>
)}
</div>
);
}
export default AuthButton;
Creating Authentication Pages:
Dedicated pages for sign-in and sign-up provide a focused area for users to manage their authentication state.
Create two new pages in your Next.js application, typically named sign-in.tsx
and sign-up.tsx
.
Use Clerk's provided components such as SignIn
and SignUp
to render the respective authentication forms.
Example for sign-in.tsx
:
tsximport { SignIn } from "@clerk/nextjs";
const SignInPage = () => {
return (
<div>
<SignIn />
{/* Additional content or styling as needed */}
</div>
);
};
export default SignInPage;
Similarly, set up the sign-up.tsx
page with the SignUp
component.
supabaseClient
within your React components for client-side interactions.Row-Level Security in Supabase: Row-level security (RLS) is a feature that allows you to control access to rows in a database table based on user attributes or other access policies. This is crucial for protecting sensitive data and ensuring that users can only access data they are permitted to see.
Setting Up RLS:
Integrating RLS with Clerk: