| name | supabase-patterns |
| description | Supabase database patterns including RLS policies, migrations, Edge Functions, and TypeScript integration. Use when working with Supabase, writing database queries, creating migrations, implementing Row-Level Security, or building Edge Functions. |
Supabase Patterns
Comprehensive patterns for Supabase development including database design, security, and TypeScript integration.
Quick Reference
RLS Policy Patterns
Basic owner-only access:
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can manage own data"
ON my_table FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
Separate policies per operation:
CREATE POLICY "Users can view own data"
ON my_table FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can insert own data"
ON my_table FOR INSERT
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can update own data"
ON my_table FOR UPDATE
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can delete own data"
ON my_table FOR DELETE
USING (auth.uid() = user_id);
Role-based access:
CREATE POLICY "Admins can view all"
ON my_table FOR SELECT
USING (
EXISTS (
SELECT 1 FROM profiles
WHERE profiles.id = auth.uid()
AND profiles.role = 'admin'
)
);
Migration Best Practices
Naming convention:
YYYYMMDDHHMMSS_descriptive_name.sql
Migration template:
CREATE TABLE IF NOT EXISTS user_profiles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
display_name TEXT,
avatar_url TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(user_id)
);
ALTER TABLE user_profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can view own profile"
ON user_profiles FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can update own profile"
ON user_profiles FOR UPDATE
USING (auth.uid() = user_id);
CREATE POLICY "Users can insert own profile"
ON user_profiles FOR INSERT
WITH CHECK (auth.uid() = user_id);
CREATE INDEX idx_user_profiles_user_id ON user_profiles(user_id);
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER user_profiles_updated_at
BEFORE UPDATE ON user_profiles
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();
TypeScript Integration
Type generation:
supabase gen types typescript --local > src/types/database.types.ts
Using generated types:
import { Database } from '@/types/database.types';
type Tables = Database['public']['Tables'];
type UserProfile = Tables['user_profiles']['Row'];
type UserProfileInsert = Tables['user_profiles']['Insert'];
type UserProfileUpdate = Tables['user_profiles']['Update'];
Typed Supabase client:
import { createClient } from '@supabase/supabase-js';
import { Database } from '@/types/database.types';
export const supabase = createClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
Edge Functions
Basic Edge Function:
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
serve(async (req) => {
const { name } = await req.json();
return new Response(
JSON.stringify({ message: `Hello ${name}!` }),
{ headers: { 'Content-Type': 'application/json' } }
);
});
Edge Function with Supabase client:
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
serve(async (req) => {
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
);
const authHeader = req.headers.get('Authorization')!;
const { data: { user }, error } = await supabase.auth.getUser(
authHeader.replace('Bearer ', '')
);
if (error || !user) {
return new Response(
JSON.stringify({ error: 'Unauthorized' }),
{ status: 401 }
);
}
const { data, error: dbError } = await supabase
.from('my_table')
.select('*')
.eq('user_id', user.id);
return new Response(
JSON.stringify({ data }),
{ headers: { 'Content-Type': 'application/json' } }
);
});
Additional Resources
For detailed patterns, see:
Common Commands
Safe Commands (Local Development)
supabase start
supabase stop
supabase status
supabase migration new <name>
supabase db push
supabase gen types typescript --local > src/types/database.types.ts
supabase functions serve <name>
DANGEROUS Commands (Require User Approval)
WARNING: The following commands affect production data. The database-admin agent enforces approval requirements for these operations.
supabase db reset
supabase db push --linked
supabase functions deploy <name>
supabase db pull
Always test locally first, then get user approval before any remote/production operations.