Beginner15 min1 prerequisite

Configure Supabase client for Next.js with server and client components, middleware, and TypeScript types.

Setup & Configuration

Set up Supabase in your Next.js project with proper client configuration for both server and client components.

Create Supabase Project

1. Sign Up / Login

Visit supabase.com and create an account.

2. Create New Project

Terminal
Dashboard  New Project

Name: my-app
Database Password: [generate strong password]
Region: [choose closest]

Save your database password—you'll need it for direct connections.

3. Get API Credentials

From Settings → API:

  • Project URL: https://xxxxx.supabase.co
  • Anon Key: Public key for client-side (safe to expose)
  • Service Role Key: Admin key (keep secret!)

Install Dependencies

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

@supabase/ssr provides helpers for Server-Side Rendering frameworks like Next.js.

Environment Variables

Create .env.local:

Terminal
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

The NEXT_PUBLIC_ prefix makes these available in browser code.

Never expose Service Role Key:

Terminal
# Only if needed for server-side admin operations
SUPABASE_SERVICE_ROLE_KEY=eyJ...  # No NEXT_PUBLIC_ prefix!

Client Configuration

Browser Client

For Client Components:

Terminal
// lib/supabase/client.ts
import { createBrowserClient } from '@supabase/ssr'

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

Usage:

Terminal
'use client'

import { createClient } from '@/lib/supabase/client'

export function UserProfile() {
  const supabase = createClient()

  async function loadUser() {
    const { data: { user } } = await supabase.auth.getUser()
    // ...
  }
}

Server Client

For Server Components and Route Handlers:

Terminal
// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'

export async function createClient() {
  const cookieStore = await cookies()

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll()
        },
        setAll(cookiesToSet) {
          try {
            cookiesToSet.forEach(({ name, value, options }) =>
              cookieStore.set(name, value, options)
            )
          } catch {
            // Called from Server Component
          }
        },
      },
    }
  )
}

Usage:

Terminal
// app/dashboard/page.tsx
import { createClient } from '@/lib/supabase/server'

export default async function Dashboard() {
  const supabase = await createClient()
  const { data: posts } = await supabase
    .from('posts')
    .select('*')

  return <PostList posts={posts} />
}

Middleware Client

For authentication middleware:

Terminal
// lib/supabase/middleware.ts
import { createServerClient } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'

export async function updateSession(request: NextRequest) {
  let supabaseResponse = NextResponse.next({
    request,
  })

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return request.cookies.getAll()
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value }) =>
            request.cookies.set(name, value)
          )
          supabaseResponse = NextResponse.next({
            request,
          })
          cookiesToSet.forEach(({ name, value, options }) =>
            supabaseResponse.cookies.set(name, value, options)
          )
        },
      },
    }
  )

  // Refresh session if expired
  await supabase.auth.getUser()

  return supabaseResponse
}
Terminal
// middleware.ts
import { type NextRequest } from 'next/server'
import { updateSession } from '@/lib/supabase/middleware'

export async function middleware(request: NextRequest) {
  return await updateSession(request)
}

export const config = {
  matcher: [
    '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
  ],
}

TypeScript Types

Generate Types

Install Supabase CLI:

Terminal
npm install -D supabase

Generate types from your database:

Terminal
npx supabase gen types typescript --project-id xxxxx > types/supabase.ts

Or add to package.json:

Terminal
{
  "scripts": {
    "types": "supabase gen types typescript --project-id xxxxx > types/supabase.ts"
  }
}

Generated Types Example

Terminal
// types/supabase.ts
export type Json =
  | string
  | number
  | boolean
  | null
  | { [key: string]: Json | undefined }
  | Json[]

export interface Database {
  public: {
    Tables: {
      posts: {
        Row: {
          id: string
          title: string
          content: string | null
          user_id: string
          created_at: string
        }
        Insert: {
          id?: string
          title: string
          content?: string | null
          user_id: string
          created_at?: string
        }
        Update: {
          id?: string
          title?: string
          content?: string | null
          user_id?: string
          created_at?: string
        }
      }
      // ... more tables
    }
  }
}

Using Types

Update client files:

Terminal
// lib/supabase/client.ts
import { createBrowserClient } from '@supabase/ssr'
import { Database } from '@/types/supabase'

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

Now queries are fully typed:

Terminal
const supabase = createClient()

// TypeScript knows posts structure
const { data: posts } = await supabase
  .from('posts')
  .select('*')

// data is typed as Post[] | null
posts?.forEach(post => {
  console.log(post.title)  // TypeScript knows this exists
})

Project Structure

Complete setup:

Terminal
project/
├── lib/
   └── supabase/
       ├── client.ts       # Browser client
       ├── server.ts       # Server client
       └── middleware.ts   # Middleware helpers
├── types/
   └── supabase.ts         # Generated database types
├── middleware.ts           # Auth middleware
├── .env.local              # Environment variables
└── package.json

Testing Connection

Create a test page:

Terminal
// app/test-supabase/page.tsx
import { createClient } from '@/lib/supabase/server'

export default async function TestPage() {
  const supabase = await createClient()

  // Test connection
  const { data, error } = await supabase
    .from('posts')
    .select('count')
    .limit(1)

  if (error) {
    return <div>Error: {error.message}</div>
  }

  return <div>Supabase connected successfully!</div>
}

Common Configuration Options

Custom Fetch

Terminal
createBrowserClient(url, key, {
  global: {
    fetch: customFetch,
  },
})

Auth Configuration

Terminal
createBrowserClient(url, key, {
  auth: {
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: true,
  },
})

Realtime Configuration

Terminal
createBrowserClient(url, key, {
  realtime: {
    params: {
      eventsPerSecond: 10,
    },
  },
})

AI Tool Integration

Lovable Setup

Lovable auto-configures Supabase:

  1. Click Supabase in settings
  2. Enter project URL and anon key
  3. Lovable generates client files automatically

Bolt.new Setup

Bolt creates Supabase config when you ask:

Terminal
"Add Supabase to this project for user authentication"

Claude Code Setup

Terminal
"Set up Supabase with proper Next.js 14 configuration
including server client, client client, middleware,
and TypeScript types generation"

Environment Variables Checklist

VariableRequiredWhere Used
NEXT_PUBLIC_SUPABASE_URLYesClient & Server
NEXT_PUBLIC_SUPABASE_ANON_KEYYesClient & Server
SUPABASE_SERVICE_ROLE_KEYNoServer admin only

Summary

  • Browser client: For Client Components
  • Server client: For Server Components and API routes
  • Middleware: For auth session refresh
  • Types: Generate from database schema
  • Environment: Use NEXT_PUBLIC_ for client-safe vars

Next Steps

Learn to create tables, run queries, and manage data with Supabase database.

Mark this lesson as complete to track your progress