Module 6: Databases & Auth with Supabase
Lesson 3

Connecting Supabase to Your Next.js App


You have a database and a table. Now you'll plug your Next.js app into it. Three things happen here: you store your keys safely, you install the Supabase library, and you let Claude create the connection code. Then you confirm it works.


Step 1: Store Your Keys in an Environment File

Your app needs to know your Supabase URL and anon key. These live in a file called .env.local, which sits in your project folder and never gets committed to GitHub.

In your terminal, inside your project folder:

touch .env.local

Open it in VS Code (code .) and add your two values from Settings → API:

NEXT_PUBLIC_SUPABASE_URL=https://your-project-id.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here

Replace both with your actual values.

Why the NEXT_PUBLIC_ prefix?

In Next.js, any environment variable starting with NEXT_PUBLIC_ is bundled into the browser, where anyone can read it. That's fine here — the anon key is designed to be public and is protected by your RLS policies.

The line you must never cross. A true secret — like the Supabase service_role key — must never get a NEXT_PUBLIC_ prefix and must never sit in client-side code. Putting the service_role key in a NEXT_PUBLIC_ variable would hand every visitor a master key that bypasses all your security rules. Only the anon key belongs in the browser.

Your .env.local should already be listed in .gitignore. Confirm it is before you ever push to GitHub.


Step 2: Install the Supabase Library

In your terminal, inside your project folder:

npm install @supabase/supabase-js @supabase/ssr

This installs:

  • @supabase/supabase-js — the main Supabase JavaScript library
  • @supabase/ssr — helpers for using Supabase with Next.js server-side rendering and cookies (needed for login to work smoothly later)

Step 3: Let Claude Create the Connection Code

You need a small utility file that initializes the Supabase connection. Let Claude write it:

Set up Supabase in my Next.js project.

Create these files:

  1. utils/supabase/client.ts — a Supabase browser client for client components
  2. utils/supabase/server.ts — a Supabase server client for server components and API routes

Use the @supabase/ssr package for the server client so cookies are handled correctly.

My environment variables are NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY.

The browser client Claude generates will look roughly like this — you don't write it, but it's good to recognize:

import { createBrowserClient } from '@supabase/ssr'

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )
}

Notice it only ever reads the two NEXT_PUBLIC_ (anon) values. That's correct — the browser client should never see anything more privileged.


⚡ Or let Claude Code do Steps 1–3

Creating the env file, installing the libraries, and writing the client code can all be handed to Claude Code in one go. In your project's Claude Code session, paste:

Set up Supabase in this Next.js project:
1. Install the @supabase/supabase-js and @supabase/ssr packages.
2. Create a .env.local with NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY
   as empty placeholders, and make sure .env.local is in .gitignore.
3. Create utils/supabase/client.ts (a browser client) and utils/supabase/server.ts
   (a server client) using the @supabase/ssr package.
Don't put any real keys in the code — I'll paste my own URL and anon key into .env.local.

You'll still open .env.local and paste in your actual URL and anon key — those are real secrets, so you handle them, not Claude. Everything else it does for you. Read the manual steps above so you understand what each file is for.


Step 4: Restart Your Dev Server

This is the single most common gotcha in this entire module. Next.js only reads .env.local when the server starts. After creating or editing that file, you must restart:

# Stop the server
Ctrl + C

# Start it again
npm run dev

If something acts strange right after you touch environment variables, restart first and re-check second.


Step 5: Test the Connection

Ask Claude to add a throwaway test so you can confirm the link works:

Add a temporary test to my home page that fetches data from the tasks table in Supabase and logs the result to the console. This is just to verify the connection — I'll replace it with real functionality next.

Then:

  1. Open http://localhost:3000
  2. Open the browser developer console (right-click → InspectConsole tab)

You should see one of two things:

  • An array of tasks — probably empty: []. That means the connection works. An empty list is success; it just means the table has no rows yet.
  • An error message — something needs fixing (see below)

Common Connection Errors

ErrorWhat it usually meansFix
Invalid API keyA typo or stray space in .env.local, or the server wasn't restartedRe-copy the anon key, check for spaces, restart the dev server
relation "tasks" does not existThe table wasn't created, or the name differsConfirm the table name in the Supabase Table Editor
Empty [] with no errorNothing wrong — RLS is on and there are no rows you can see yetAdd a row, or move on to the CRUD lesson

Once you've confirmed it works, ask Claude to remove the temporary test.


Client vs. Server Components (The Short Version)

Next.js has two kinds of components, and they use two different Supabase clients:

Client ComponentsServer Components
Marked with"use client" at the topnothing (the default)
Runs inthe browserthe server
Can useuseState, useEffect, click handlersdirect data fetching, no loading spinner needed
Supabase clientthe browser clientthe server client
Securityuses the public anon keykeys never reach the browser

For your first app, using client components throughout is perfectly fine. As you get comfortable, Claude can help you move heavy data fetching to server components for better performance and security.


Telling Claude What to Build Next

Once the connection is live, describe the feature you want wired to real data. A template that works well:

Now that Supabase is connected, update [specific feature] to use real data.

Current behavior: tasks are hardcoded example data. Desired behavior: tasks are fetched from the tasks table in Supabase.

The tasks table has: id, title, done, user_id, created_at.

For now, skip authentication — just fetch all tasks (no user filtering yet). We'll add auth later.


Summary

  • Store your URL and anon key in .env.local (with the NEXT_PUBLIC_ prefix) and never commit it.
  • Only the anon key belongs in the browser; the service_role key never gets a NEXT_PUBLIC_ prefix or client-side code.
  • Install with npm install @supabase/supabase-js @supabase/ssr.
  • Let Claude create the browser and server client utility files.
  • Restart the dev server after any change to .env.local.
  • Test by logging a fetch to the console — an empty [] means success.