Supabase Client Setup
> **Skill Purpose:** Backend-as-a-Service client integration and configuration patterns
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:
- Define backend service requirements and scope
- Configure client authentication and security
- Set up data access patterns and operations
- Establish real-time and event-driven functionality
- 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
- Use type-safe database access - Generated TypeScript types
- Separate client/server clients - Proper context usage
- Implement proper error handling - User-friendly messages
- Use authentication middleware - Route protection
- Implement real-time subscriptions - Live updates
- Use environment variables - Secure configuration
- Generate types regularly - Keep types in sync
- 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
