| name | supabase-patterns |
| description | Supabase: Auth, Realtime, Storage, Edge Functions, Row Level Security, Postgres integration patterns. |
| triggers | {"extensions":[".ts",".tsx"],"directories":["supabase/","lib/supabase/"],"filenames":["supabase.ts","supabase.config.ts"],"keywords":["supabase","createClient","RLS","realtime","storage bucket","edge function"]} |
| auto_load_when | Using Supabase as backend (auth, database, storage, realtime) |
| agent | infra-specialist |
| tools | ["Read","Write","Bash"] |
Supabase Patterns
Version: Supabase JS v2 | Focus: Auth, RLS, Realtime, Storage, Edge Functions
1. Client Setup
import { createBrowserClient } from '@supabase/ssr';
export const createClient = () =>
createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
import { createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';
export const createClient = () => {
const cookieStore = cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{ cookies: { getAll: () => cookieStore.getAll(), setAll: (c) => c.forEach(({ name, value, options }) => cookieStore.set(name, value, options)) } }
);
};
2. Auth Patterns
const { data, error } = await supabase.auth.signUp({ email, password });
const { data, error } = await supabase.auth.signInWithPassword({ email, password });
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: { redirectTo: `${origin}/auth/callback` },
});
const { data: { user } } = await supabase.auth.getUser();
export async function middleware(req: NextRequest) {
const { supabase, response } = createMiddlewareClient(req);
await supabase.auth.getSession();
return response;
}
3. Row Level Security (RLS)
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
CREATE POLICY "own_rows" ON posts
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "insert_own" ON posts
FOR INSERT WITH CHECK (auth.uid() = user_id);
Rule: Every table needs RLS. Default deny — add explicit ALLOW policies.
4. Realtime Subscriptions
const channel = supabase
.channel('posts_changes')
.on('postgres_changes',
{ event: '*', schema: 'public', table: 'posts', filter: `user_id=eq.${userId}` },
(payload) => console.log(payload)
)
.subscribe();
return () => supabase.removeChannel(channel);
const room = supabase.channel('room1');
room.on('broadcast', { event: 'cursor' }, ({ payload }) => handleCursor(payload));
room.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
await room.send({ type: 'broadcast', event: 'cursor', payload: { x, y } });
}
});
5. Storage
const { data, error } = await supabase.storage
.from('avatars')
.upload(`${userId}/avatar.png`, file, { upsert: true });
const { data: { publicUrl } } = supabase.storage
.from('avatars')
.getPublicUrl(`${userId}/avatar.png`);
const { data, error } = await supabase.storage
.from('private-docs')
.createSignedUrl('file.pdf', 3600);
6. Edge Functions
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 body = await req.json();
return new Response(JSON.stringify({ ok: true }), { headers: { 'Content-Type': 'application/json' } });
});
Quick Reference
| Need | Approach |
|---|
| Browser auth state | createBrowserClient + onAuthStateChange |
| Server auth check | createServerClient + getUser() |
| Protected routes | Middleware with getSession() |
| Data access control | RLS policies, not application-level |
| Real-time data | channel.on('postgres_changes', ...) |
| File uploads | Storage SDK + bucket policies |
| Cron / webhooks | Edge Functions with Deno |
| Admin operations | Service role key (server-only, never client) |
Anti-Patterns
❌ Using service_role key in browser/client code
✅ Service role only in Edge Functions or trusted server
❌ Skipping RLS, filtering in application code
✅ Always enable RLS; let database enforce access
❌ Fetching data in useEffect with supabase client
✅ Server Components + server client for initial data
❌ One channel per component (creates many connections)
✅ Shared channel per feature; remove on unmount
🌍 Universal Language Support
- Turkish Native: This skill natively supports Turkish. If the user prompt is in Turkish, all analysis, formatting, and output MUST be entirely in Turkish.