Skip to content
Supabase Client Setup logo

Supabase Client Setup

> **Skill Purpose:** Backend-as-a-Service client integration and configuration patterns

Coverage-Creatives/zeus0installs0stars

SKILL.md

Full skill instructions

Supabase Client Setup

Skill Purpose: Backend-as-a-Service client integration and configuration patterns


Core Skill Pattern

Objective: Establish secure and efficient backend service integration with authentication, data operations, and real-time capabilities.

Universal Pattern:

  1. Define backend service requirements and scope
  2. Configure client authentication and security
  3. Set up data access patterns and operations
  4. Establish real-time and event-driven functionality
  5. Create error handling and offline procedures

Key Decisions (Project-Specific):

  • Backend service selection and feature scope
  • Authentication methods and security requirements
  • Data access patterns and caching strategies
  • Real-time features and event handling needs
  • Error handling and offline functionality requirements

Project-Specific Implementation Notes

Customize per project:

  • Service selection based on backend requirements
  • Authentication complexity based on user needs
  • Data patterns based on application complexity
  • Real-time features based on user experience requirements
  • Error handling based on reliability needs

Example Implementation (Supabase with Next.js Pattern)

Note: This is an example pattern using Supabase. Adapt backend service and configuration based on your specific project requirements and technology stack.

Prerequisites (Example)

  • Project initialized
  • Backend service selected and configured
  • Authentication requirements defined

Example: Supabase Backend Service Implementation

Framework-Specific Example: This demonstrates the pattern using Supabase with Next.js. Adapt for your backend service and framework.

1. Install Supabase Dependencies

# Install Supabase client and auth helpers
npm install @supabase/​supabase-js @supabase/​auth-helpers-nextjs @supabase/​auth-helpers-react

# Install additional Supabase packages
npm install @supabase/​auth-ui-react @supabase/​auth-ui-shared
npm install @supabase/​functions-js

# Install TypeScript types
npm install -D @types/​supabase

2. Create Supabase Client Configuration

Create src/​lib/​supabase/​client.ts:

import { createClientComponentClient } from '@supabase/​auth-helpers-nextjs';
import { Database } from '@/​types/​database';

// Client-side Supabase client
export function createClient() {
  return createClientComponentClient<Database>();
}

// Singleton client for client-side usage
let supabaseClient: ReturnType<typeof createClient> | null = null;

export function getSupabaseClient() {
  if (!supabaseClient) {
    supabaseClient = createClient();
  }
  return supabaseClient;
}

Create src/​lib/​supabase/​server.ts:

import { createServerComponentClient } from '@supabase/​auth-helpers-nextjs';
import { cookies } from 'next/​headers';
import { Database } from '@/​types/​database';

// Server-side Supabase client
export function createServerClient() {
  const cookieStore = cookies();
  return createServerComponentClient<Database>({
    cookies: () => cookieStore,
  });
}

// Server-side Supabase client for API routes
export function createApiRouteClient() {
  return createServerComponentClient<Database>({
    cookies: () => cookies(),
  });
}

3. Create Database Types

Create src/​types/​database.ts:

// Supabase database types
export interface Database {
  public: {
    Tables: {
      users: {
        Row: {
          id: string;
          email: string;
          name: string | null;
          avatar_url: string | null;
          created_at: string;
          updated_at: string;
        };
        Insert: {
          id?: string;
          email: string;
          name?: string | null;
          avatar_url?: string | null;
          created_at?: string;
          updated_at?: string;
        };
        Update: {
          id?: string;
          email?: string;
          name?: string | null;
          avatar_url?: string | null;
          updated_at?: string;
        };
      };
      profiles: {
        Row: {
          id: string;
          user_id: string;
          username: string | null;
          full_name: string | null;
          avatar_url: string | null;
          website: string | null;
          created_at: string;
          updated_at: string;
        };
        Insert: {
          id?: string;
          user_id: string;
          username?: string | null;
          full_name?: string | null;
          avatar_url?: string | null;
          website?: string | null;
          created_at?: string;
          updated_at?: string;
        };
        Update: {
          id?: string;
          user_id?: string;
          username?: string | null;
          full_name?: string | null;
          avatar_url?: string | null;
          website?: string | null;
          updated_at?: string;
        };
      };
      posts: {
        Row: {
          id: string;
          title: string;
          content: string | null;
          published: boolean;
          author_id: string;
          created_at: string;
          updated_at: string;
        };
        Insert: {
          id?: string;
          title: string;
          content?: string | null;
          published?: boolean;
          author_id: string;
          created_at?: string;
          updated_at?: string;
        };
        Update: {
          id?: string;
          title?: string;
          content?: string | null;
          published?: boolean;
          author_id?: string;
          updated_at?: string;
        };
      };
    };
    Views: {
      posts_with_author: {
        Row: {
          id: string;
          title: string;
          content: string | null;
          published: boolean;
          author_id: string;
          author_name: string | null;
          author_avatar: string | null;
          created_at: string;
          updated_at: string;
        };
      };
    };
    Functions: {
      get_user_posts: {
        Args: {
          user_id: string;
        };
        Returns: {
          id: string;
          title: string;
          content: string | null;
          published: boolean;
          created_at: string;
        }[];
      };
    };
    Enums: {
      post_status: 'draft' | 'published' | 'archived';
      user_role: 'admin' | 'user' | 'moderator';
    };
  };
}

// Type helpers
export type Tables<T extends keyof Database['public']['Tables']> = Database['public']['Tables'][T];
export type TablesInsert<T extends keyof Database['public']['Tables']> = Tables<T>['Insert'];
export type TablesUpdate<T extends keyof Database['public']['Tables']> = Tables<T>['Update'];
export type TablesRow<T extends keyof Database['public']['Tables']> = Tables<T>['Row'];

// View types
export type Views<T extends keyof Database['public']['Views']> = Database['public']['Views'][T];
export type ViewsRow<T extends keyof Database['public']['Views']> = Views<T>['Row'];

// Function types
export type Functions<T extends keyof Database['public']['Functions']> = Database['public']['Functions'][T];
export type FunctionsArgs<T extends keyof Database['public']['Functions']> = Functions<T>['Args'];
export type FunctionsReturns<T extends keyof Database['public']['Functions']> = Functions<T>['Returns'];

// Enum types
export type Enums<T extends keyof Database['public']['Enums']> = Database['public']['Enums'][T];

4. Create Authentication Utilities

Create src/​lib/​auth.ts:

import { createClient } from '@/​lib/​supabase/​client';
import { createServerClient } from '@/​lib/​supabase/​server';
import { Database } from '@/​types/​database';
import type { User, Session, AuthError } from '@supabase/​supabase-js';

// Client-side authentication
export async function signIn(email: string, password: string) {
  const supabase = createClient();
  
  const { data, error } = await supabase.auth.signInWithPassword({
    email,
    password,
  });
  
  return { data, error };
}

export async function signUp(email: string, password: string, options?: {
  data?: {
    name?: string;
    username?: string;
  };
}) {
  const supabase = createClient();
  
  const { data, error } = await supabase.auth.signUp({
    email,
    password,
    options: {
      data: options?.data,
    },
  });
  
  return { data, error };
}

export async function signOut() {
  const supabase = createClient();
  
  const { error } = await supabase.auth.signOut();
  
  return { error };
}

export async function resetPassword(email: string) {
  const supabase = createClient();
  
  const { data, error } = await supabase.auth.resetPasswordForEmail(email);
  
  return { data, error };
}

export async function updatePassword(newPassword: string) {
  const supabase = createClient();
  
  const { data, error } = await supabase.auth.updateUser({
    password: newPassword,
  });
  
  return { data, error };
}

// Server-side authentication
export async function getServerUser() {
  const supabase = createServerClient();
  
  const { data: { user } } = await supabase.auth.getUser();
  
  return user;
}

export async function getServerSession() {
  const supabase = createServerClient();
  
  const { data: { session } } = await supabase.auth.getSession();
  
  return session;
}

// Authentication hooks
export async function requireAuth() {
  const user = await getServerUser();
  
  if (!user) {
    throw new Error('Authentication required');
  }
  
  return user;
}

export async function requireRole(role: 'admin' | 'moderator') {
  const user = await requireAuth();
  
  // Check user role from profiles table
  const supabase = createServerClient();
  const { data: profile } = await supabase
    .from('profiles')
    .select('role')
    .eq('user_id', user.id)
    .single();
  
  if (!profile || profile.role !== role) {
    throw new Error(`Access denied. Required role: ${role}`);
  }
  
  return user;
}

// Authentication utilities
export function isAuthError(error: any): error is AuthError {
  return error?.name === 'AuthError';
}

export function getAuthErrorMessage(error: AuthError): string {
  switch (error.message) {
    case 'Invalid login credentials':
      return 'Invalid email or password';
    case 'Email not confirmed':
      return 'Please confirm your email address';
    case 'Password should be at least 6 characters':
      return 'Password must be at least 6 characters long';
    case 'User already registered':
      return 'An account with this email already exists';
    case 'Invalid email':
      return 'Please enter a valid email address';
    default:
      return error.message;
  }
}

5. Create Database Utilities

Create src/​lib/​database.ts:

import { createClient, createServerClient } from '@/​lib/​supabase/​client';
import { Database, TablesInsert, TablesUpdate } from '@/​types/​database';
import { PostgrestError } from '@supabase/​supabase-js';

// Generic database operations
export class DatabaseService<T extends keyof Database['public']['Tables']> {
  private tableName: T;
  private isServer: boolean;

  constructor(tableName: T, isServer = false) {
    this.tableName = tableName;
    this.isServer = isServer;
  }

  private getClient() {
    return this.isServer ? createServerClient() : createClient();
  }

  // Read operations
  async select<Columns extends keyof Database['public']['Tables'][T]['Row']>(
    columns?: Columns[]
  ) {
    const supabase = this.getClient();
    
    let query = supabase.from(this.tableName).select(columns?.join(','));
    
    const { data, error } = await query;
    
    return { data, error };
  }

  async selectById(id: string) {
    const supabase = this.getClient();
    
    const { data, error } = await supabase
      .from(this.tableName)
      .select('*')
      .eq('id', id)
      .single();
    
    return { data, error };
  }

  async selectByColumn<Column extends keyof Database['public']['Tables'][T]['Row']>(
    column: Column,
    value: Database['public']['Tables'][T]['Row'][Column]
  ) {
    const supabase = this.getClient();
    
    const { data, error } = await supabase
      .from(this.tableName)
      .select('*')
      .eq(column as string, value as string);
    
    return { data, error };
  }

  // Write operations
  async insert(data: TablesInsert<T>) {
    const supabase = this.getClient();
    
    const { data: result, error } = await supabase
      .from(this.tableName)
      .insert(data)
      .select()
      .single();
    
    return { data: result, error };
  }

  async update(id: string, data: TablesUpdate<T>) {
    const supabase = this.getClient();
    
    const { data: result, error } = await supabase
      .from(this.tableName)
      .update(data)
      .eq('id', id)
      .select()
      .single();
    
    return { data: result, error };
  }

  async delete(id: string) {
    const supabase = this.getClient();
    
    const { data, error } = await supabase
      .from(this.tableName)
      .delete()
      .eq('id', id)
      .select()
      .single();
    
    return { data, error };
  }

  // Advanced operations
  async upsert(data: TablesInsert<T>) {
    const supabase = this.getClient();
    
    const { data: result, error } = await supabase
      .from(this.tableName)
      .upsert(data)
      .select()
      .single();
    
    return { data: result, error };
  }

  async count() {
    const supabase = this.getClient();
    
    const { count, error } = await supabase
      .from(this.tableName)
      .select('*', { count: 'exact', head: true });
    
    return { count, error };
  }
}

// Specific table services
export const usersService = new DatabaseService('users');
export const profilesService = new DatabaseService('profiles');
export const postsService = new DatabaseService('posts');

// Server-side services
export const serverUsersService = new DatabaseService('users', true);
export const serverProfilesService = new DatabaseService('profiles', true);
export const serverPostsService = new DatabaseService('posts', true);

// Database utilities
export function isPostgrestError(error: any): error is PostgrestError {
  return error?.code !== undefined;
}

export function getDatabaseErrorMessage(error: PostgrestError): string {
  switch (error.code) {
    case '23505':
      return 'This record already exists';
    case '23503':
      return 'Referenced record does not exist';
    case '23514':
      return 'Check constraint failed';
    case '42501':
      return 'Insufficient privileges';
    case '42P01':
      return 'Table not found';
    case '42703':
      return 'Column not found';
    default:
      return error.message || 'Database operation failed';
  }
}

// Query builders
export function buildQuery<T extends keyof Database['public']['Tables']>(
  tableName: T,
  isServer = false
) {
  const supabase = isServer ? createServerClient() : createClient();
  return supabase.from(tableName);
}

// Real-time subscriptions
export function subscribeToTable<T extends keyof Database['public']['Tables']>(
  tableName: T,
  event: 'INSERT' | 'UPDATE' | 'DELETE' | '*',
  callback: (payload: any) => void
) {
  const supabase = createClient();
  
  return supabase
    .channel(`${tableName}-changes`)
    .on('postgres_changes', 
      { event, schema: 'public', table: tableName },
      callback
    )
    .subscribe();
}

// Edge functions
export async function callEdgeFunction(
  functionName: string,
  payload?: any,
  options?: {
    headers?: Record<string, string>;
  }
) {
  const supabase = createClient();
  
  const { data, error } = await supabase.functions.invoke(functionName, {
    body: payload,
    headers: options?.headers,
  });
  
  return { data, error };
}

6. Create Authentication Context

Create src/​contexts/​auth-context.tsx:

'use client';

import { createContext, useContext, useEffect, useState } from 'react';
import { User, Session } from '@supabase/​supabase-js';
import { createClient } from '@/​lib/​supabase/​client';

interface AuthContextType {
  user: User | null;
  session: Session | null;
  loading: boolean;
  signIn: (email: string, password: string) => Promise<{ error?: string }>;
  signUp: (email: string, password: string, data?: any) => Promise<{ error?: string }>;
  signOut: () => Promise<void>;
  resetPassword: (email: string) => Promise<{ error?: string }>;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [session, setSession] = useState<Session | null>(null);
  const [loading, setLoading] = useState(true);

  const supabase = createClient();

  useEffect(() => {
    // Get initial session
    const getInitialSession = async () => {
      const { data: { session } } = await supabase.auth.getSession();
      setSession(session);
      setUser(session?.user ?? null);
      setLoading(false);
    };

    getInitialSession();

    // Listen for auth changes
    const {
      data: { subscription },
    } = supabase.auth.onAuthStateChange((_event, session) => {
      setSession(session);
      setUser(session?.user ?? null);
      setLoading(false);
    });

    return () => subscription.unsubscribe();
  }, [supabase]);

  const signIn = async (email: string, password: string) => {
    const { error } = await supabase.auth.signInWithPassword({
      email,
      password,
    });
    return { error: error?.message };
  };

  const signUp = async (email: string, password: string, data?: any) => {
    const { error } = await supabase.auth.signUp({
      email,
      password,
      options: { data },
    });
    return { error: error?.message };
  };

  const signOut = async () => {
    await supabase.auth.signOut();
  };

  const resetPassword = async (email: string) => {
    const { error } = await supabase.auth.resetPasswordForEmail(email);
    return { error: error?.message };
  };

  const value = {
    user,
    session,
    loading,
    signIn,
    signUp,
    signOut,
    resetPassword,
  };

  return (
    <AuthContext.Provider value={value}>
      {children}
    </​AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}

7. Create Authentication Components

Create src/​components/​auth/​auth-form.tsx:

'use client';

import { useState } from 'react';
import { useAuth } from '@/​contexts/​auth-context';
import { Button } from '@/​components/​ui/​button';
import { Input } from '@/​components/​ui/​input';
import { Label } from '@/​components/​ui/​label';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/​components/​ui/​card';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/​components/​ui/​tabs';

export function AuthForm() {
  const { signIn, signUp, loading } = useAuth();
  const [error, setError] = useState<string>('');
  const [isLoading, setIsLoading] = useState(false);

  const handleSignIn = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    setIsLoading(true);
    setError('');

    const formData = new FormData(e.currentTarget);
    const email = formData.get('email') as string;
    const password = formData.get('password') as string;

    const { error } = await signIn(email, password);
    if (error) {
      setError(error);
    }
    setIsLoading(false);
  };

  const handleSignUp = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    setIsLoading(true);
    setError('');

    const formData = new FormData(e.currentTarget);
    const email = formData.get('email') as string;
    const password = formData.get('password') as string;
    const name = formData.get('name') as string;

    const { error } = await signUp(email, password, { name });
    if (error) {
      setError(error);
    }
    setIsLoading(false);
  };

  if (loading) {
    return <div>Loading...</​div>;
  }

  return (
    <Card className="w-full max-w-md mx-auto">
      <CardHeader>
        <CardTitle>Authentication</​CardTitle>
        <CardDescription>
          Sign in to your account or create a new one
        </​CardDescription>
      </​CardHeader>
      <CardContent>
        <Tabs defaultValue="signin" className="w-full">
          <TabsList className="grid w-full grid-cols-2">
            <TabsTrigger value="signin">Sign In</​TabsTrigger>
            <TabsTrigger value="signup">Sign Up</​TabsTrigger>
          </​TabsList>
          
          <TabsContent value="signin">
            <form onSubmit={handleSignIn} className="space-y-4">
              <div className="space-y-2">
                <Label htmlFor="signin-email">Email</​Label>
                <Input
                  id="signin-email"
                  name="email"
                  type="email"
                  placeholder="Enter your email"
                  required
                />
              </​div>
              <div className="space-y-2">
                <Label htmlFor="signin-password">Password</​Label>
                <Input
                  id="signin-password"
                  name="password"
                  type="password"
                  placeholder="Enter your password"
                  required
                />
              </​div>
              {error && (
                <div className="text-sm text-destructive">{error}</​div>
              )}
              <Button type="submit" className="w-full" disabled={isLoading}>
                {isLoading ? 'Signing in...' : 'Sign In'}
              </​Button>
            </​form>
          </​TabsContent>
          
          <TabsContent value="signup">
            <form onSubmit={handleSignUp} className="space-y-4">
              <div className="space-y-2">
                <Label htmlFor="signup-name">Name</​Label>
                <Input
                  id="signup-name"
                  name="name"
                  type="text"
                  placeholder="Enter your name"
                />
              </​div>
              <div className="space-y-2">
                <Label htmlFor="signup-email">Email</​Label>
                <Input
                  id="signup-email"
                  name="email"
                  type="email"
                  placeholder="Enter your email"
                  required
                />
              </​div>
              <div className="space-y-2">
                <Label htmlFor="signup-password">Password</​Label>
                <Input
                  id="signup-password"
                  name="password"
                  type="password"
                  placeholder="Enter your password"
                  required
                />
              </​div>
              {error && (
                <div className="text-sm text-destructive">{error}</​div>
              )}
              <Button type="submit" className="w-full" disabled={isLoading}>
                {isLoading ? 'Creating account...' : 'Sign Up'}
              </​Button>
            </​form>
          </​TabsContent>
        </​Tabs>
      </​CardContent>
    </​Card>
  );
}

8. Create Middleware for Authentication

Create src/​middleware.ts:

import { createMiddlewareClient } from '@supabase/​auth-helpers-nextjs';
import { NextResponse } from 'next/​server';
import type { NextRequest } from 'next/​server';

export async function middleware(req: NextRequest) {
  const res = NextResponse.next();
  const supabase = createMiddlewareClient({ req, res });

  // Refresh session if expired - required for Server Components
  // https://supabase.com/docs/guides/auth/auth-helpers/nextjs#managing-session-with-middleware
  await supabase.auth.getSession();

  // Protected routes
  const protectedRoutes = ['/​dashboard', '/​profile', '/​settings'];
  const authRoutes = ['/​auth/​signin', '/​auth/​signup'];
  
  const { data: { session } } = await supabase.auth.getSession();
  const isProtectedRoute = protectedRoutes.some(route => 
    req.nextUrl.pathname.startsWith(route)
  );
  const isAuthRoute = authRoutes.some(route => 
    req.nextUrl.pathname.startsWith(route)
  );

  // Redirect authenticated users away from auth pages
  if (session && isAuthRoute) {
    return NextResponse.redirect(new URL('/​dashboard', req.url));
  }

  // Redirect unauthenticated users away from protected pages
  if (!session && isProtectedRoute) {
    return NextResponse.redirect(new URL('/​auth/​signin', req.url));
  }

  return res;
}

export const config = {
  matcher: [
    /​*
     * Match all request paths except for the ones starting with:
     * - _next/​static (static files)
     * - _next/​image (image optimization files)
     * - favicon.ico (favicon file)
     * Feel free to modify this pattern to include more paths.
     */
    '/((?!_next/​static|_next/​image|favicon.ico).*)',
  ],
};

9. Create Database Migration Scripts

Create scripts/​supabase-setup.sh:

#!/​bin/​bash

# Supabase setup script for Zeus framework
echo "🚀 Setting up Supabase for Zeus framework..."

# Check if Supabase CLI is installed
if ! command -v supabase &> /​dev/​null; then
    echo "❌ Supabase CLI not found. Please install it first:"
    echo "npm install -g supabase"
    exit 1
fi

# Initialize Supabase project
if [ ! -d "supabase" ]; then
    echo "Initializing Supabase project..."
    supabase init
fi

# Link to existing project or create new one
echo "Do you want to link to an existing Supabase project? (y/​n)"
read -r link_existing

if [ "$link_existing" = "y" ]; then
    echo "Enter your Supabase project reference:"
    read -r project_ref
    supabase link --project-ref "$project_ref"
else
    echo "Creating new Supabase project..."
    supabase start
fi

# Generate types
echo "Generating TypeScript types..."
supabase gen types typescript --local > src/​types/​database.ts

# Start local development
echo "Starting local Supabase development..."
supabase start

echo "✅ Supabase setup complete!"
echo ""
echo "Next steps:"
echo "1. Update your .env.local with the local Supabase URL and keys"
echo "2. Run database migrations: supabase db push"
echo "3. Seed your database: supabase db seed"

Create scripts/​generate-types.sh:

#!/​bin/​bash

# Generate TypeScript types from Supabase schema
echo "🔄 Generating TypeScript types from Supabase..."

# Check if Supabase is linked
if ! supabase status &> /​dev/​null; then
    echo "❌ Supabase not linked. Please run 'npm run supabase:setup' first."
    exit 1
fi

# Generate types
supabase gen types typescript --local > src/​types/​database.ts

echo "✅ TypeScript types generated successfully!"
echo "📍 Types saved to: src/​types/​database.ts"

10. Update Package.json Scripts

Update package.json scripts:

{
  "scripts": {
    "supabase:setup": "bash scripts/​supabase-setup.sh",
    "supabase:start": "supabase start",
    "supabase:stop": "supabase stop",
    "supabase:reset": "supabase db reset",
    "supabase:migrate": "supabase db push",
    "supabase:seed": "supabase db seed",
    "supabase:types": "bash scripts/​generate-types.sh",
    "supabase:status": "supabase status",
    "supabase:logs": "supabase logs"
  }
}

Code Examples

Using Supabase in Components

// Client-side component
'use client';

import { useState, useEffect } from 'react';
import { createClient } from '@/​lib/​supabase/​client';
import { Database } from '@/​types/​database';

export function PostsList() {
  const [posts, setPosts] = useState<Database['public']['Tables']['posts']['Row'][]>([]);
  const [loading, setLoading] = useState(true);
  const supabase = createClient();

  useEffect(() => {
    fetchPosts();
    
    // Subscribe to real-time updates
    const subscription = supabase
      .channel('posts-changes')
      .on('postgres_changes', 
        { event: '*', schema: 'public', table: 'posts' },
        (payload) => {
          fetchPosts();
        }
      )
      .subscribe();

    return () => subscription.unsubscribe();
  }, []);

  async function fetchPosts() {
    const { data, error } = await supabase
      .from('posts')
      .select('*')
      .order('created_at', { ascending: false });

    if (error) {
      console.error('Error fetching posts:', error);
    } else {
      setPosts(data || []);
    }
    setLoading(false);
  }

  if (loading) return <div>Loading posts...</​div>;

  return (
    <div>
      {posts.map(post => (
        <div key={post.id}>
          <h3>{post.title}</​h3>
          <p>{post.content}</​p>
        </​div>
      ))}
    </​div>
  );
}

Server-Side Usage

// Server component
import { createServerClient } from '@/​lib/​supabase/​server';
import { Database } from '@/​types/​database';

export default async function UserProfile({ userId }: { userId: string }) {
  const supabase = createServerClient();
  
  const { data: profile } = await supabase
    .from('profiles')
    .select('*')
    .eq('user_id', userId)
    .single();

  if (!profile) {
    return <div>Profile not found</​div>;
  }

  return (
    <div>
      <h1>{profile.full_name || profile.username}</​h1>
      <p>{profile.website}</​p>
    </​div>
  );
}

// API route
import { createApiRouteClient } from '@/​lib/​supabase/​server';
import { NextRequest, NextResponse } from 'next/​server';

export async function POST(request: NextRequest) {
  const supabase = createApiRouteClient();
  const { title, content } = await request.json();
  
  const { data, error } = await supabase
    .from('posts')
    .insert({ title, content })
    .select()
    .single();

  if (error) {
    return NextResponse.json({ error: error.message }, { status: 400 });
  }

  return NextResponse.json({ data });
}

Authentication Usage

// Protected component
'use client';

import { useAuth } from '@/​contexts/​auth-context';
import { Button } from '@/​components/​ui/​button';

export function ProtectedComponent() {
  const { user, signOut } = useAuth();

  if (!user) {
    return <div>Please sign in to access this content</​div>;
  }

  return (
    <div>
      <h1>Welcome, {user.email}!</​h1>
      <Button onClick={signOut}>Sign Out</​Button>
    </​div>
  );
}

// Server-side protected route
import { requireAuth } from '@/​lib/​auth';
import { redirect } from 'next/​navigation';

export default async function DashboardPage() {
  const user = await requireAuth();
  
  return (
    <div>
      <h1>Dashboard</​h1>
      <p>Welcome, {user.email}!</​p>
    </​div>
  );
}

Configuration Templates

Complete Database Types

export interface Database {
  public: {
    Tables: {
      users: {
        Row: { id: string; email: string; name: string | null; avatar_url: string | null; created_at: string; updated_at: string; };
        Insert: { id?: string; email: string; name?: string | null; avatar_url?: string | null; created_at?: string; updated_at?: string; };
        Update: { id?: string; email?: string; name?: string | null; avatar_url?: string | null; updated_at?: string; };
      };
      profiles: {
        Row: { id: string; user_id: string; username: string | null; full_name: string | null; avatar_url: string | null; website: string | null; created_at: string; updated_at: string; };
        Insert: { id?: string; user_id: string; username?: string | null; full_name?: string | null; avatar_url?: string | null; website?: string | null; created_at?: string; updated_at?: string; };
        Update: { id?: string; user_id?: string; username?: string | null; full_name?: string | null; avatar_url?: string | null; website?: string | null; updated_at?: string; };
      };
    };
    Views: {
      posts_with_author: {
        Row: { id: string; title: string; content: string | null; published: boolean; author_id: string; author_name: string | null; author_avatar: string | null; created_at: string; updated_at: string; };
      };
    };
    Functions: {
      get_user_posts: {
        Args: { user_id: string; };
        Returns: { id: string; title: string; content: string | null; published: boolean; created_at: string; }[];
      };
    };
    Enums: {
      post_status: 'draft' | 'published' | 'archived';
      user_role: 'admin' | 'user' | 'moderator';
    };
  };
}

Best Practices

  1. Use type-safe database access - Generated TypeScript types
  2. Separate client/​server clients - Proper context usage
  3. Implement proper error handling - User-friendly messages
  4. Use authentication middleware - Route protection
  5. Implement real-time subscriptions - Live updates
  6. Use environment variables - Secure configuration
  7. Generate types regularly - Keep types in sync
  8. Use proper session management - Refresh tokens

Stop Conditions

STOP and report if:

  • Supabase client initialization fails
  • Authentication setup errors
  • Database connection issues
  • Type generation fails
  • Middleware configuration errors

Expected Outcomes:

  • Supabase clients configured correctly
  • Authentication working properly
  • Database operations functional
  • Real-time subscriptions working
  • TypeScript types generated

Verification Checklist

  • Supabase dependencies installed
  • Client and server clients configured
  • Database types generated
  • Authentication utilities working
  • Database service functional
  • Authentication context working
  • Middleware configured
  • Setup scripts functional
  • Package.json scripts updated
  • Environment variables configured

Version: 1.0.0 Last Updated: 2026-01-31 Skill Category: Architecture - Scaffolding