Skip to main content Início Criadores diegosouzapw awesome-omni-skill supabase-expert
supabase-expert Advanced Supabase integration specialist for Auth, Database (PostgreSQL/RLS), Storage, Realtime, Edge Functions, and AI/Vector features. Use when implementing Supabase features, debugging Supabase issues, setting up RLS policies, creating database schemas, building auth flows, optimizing Supabase queries, migrating to Supabase, or architecting Supabase-based applications. Invoke for Supabase client setup, type generation, migration creation, performance tuning, security audits, or Supabase best practices. Handles Next.js, React, Vue, Svelte, and server-side integrations.
Ir para a instalação Skills Marketplace Descubra e explore skills de IA criadas pela comunidade.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Copiar promptMostrar detalhes do prompt Um comando direto ignora o prompt de revisão. Verifique a origem antes de executá-lo.
npx skills add https://github.com/diegosouzapw/awesome-omni-skill --skill supabase-expertO comando permanece em uma só linha. Role horizontalmente para revisá-lo antes de copiar.
Prefere uma cópia local? Baixe os arquivos disponíveis atualmente no SkillsMP.
Baixar Zip Baixando... Ocupações relacionadas SOC
Baseado na classificação ocupacional SOC
Explorador de arquivos
2 arquivos name supabase-expert description Advanced Supabase integration specialist for Auth, Database (PostgreSQL/RLS), Storage, Realtime, Edge Functions, and AI/Vector features. Use when implementing Supabase features, debugging Supabase issues, setting up RLS policies, creating database schemas, building auth flows, optimizing Supabase queries, migrating to Supabase, or architecting Supabase-based applications. Invoke for Supabase client setup, type generation, migration creation, performance tuning, security audits, or Supabase best practices. Handles Next.js, React, Vue, Svelte, and server-side integrations. allowed-tools Read, Write, Edit, Bash, Grep, Glob model sonnet
Supabase Expert Skill - Advanced Implementation Guide
Purpose
This is a comprehensive, production-grade skill for working with Supabase across all aspects of modern application development. It provides:
Deep Technical Expertise : Advanced patterns for complex use cases
Framework Integration : Specific implementations for Next.js, React, Vue, Svelte
Production Readiness : Security audits, performance optimization, error handling
Architecture Guidance : Multi-tenancy, scaling strategies, migration patterns
Real-world Solutions : Battle-tested patterns from production applications
This skill leverages the complete Supabase documentation (2,190 pages) to provide accurate, up-to-date guidance across:
Authentication & Authorization (30+ auth methods)
PostgreSQL Database & RLS (Advanced query optimization)
Storage & CDN (File management at scale)
Realtime (WebSocket subscriptions, presence, broadcast)
Edge Functions (Deno runtime, serverless patterns)
AI/Vector (Embeddings, semantic search, RAG)
When to Use
Core Implementation Tasks
Setting up Supabase client configuration and TypeScript types
Implementing authentication (social login, magic links, SSO, MFA, anonymous auth)
Creating PostgreSQL schemas with Row Level Security (RLS)
Building realtime features with Supabase Realtime
Implementing file storage with Supabase Storage
Creating or debugging Edge Functions
Working with vector embeddings and AI features
Setting up local development with Supabase CLI
Creating and managing database migrations
Advanced & Production Tasks
Troubleshooting Supabase connection or query issues
Optimizing Supabase queries and performance
Implementing multi-tenancy with RLS
Security audits and hardening
Migration from Firebase, Parse, or other BaaS
Architecting scalable Supabase applications
Connection pooling and serverless optimization
Implementing complex authorization patterns
Setting up CI/CD with Supabase
Monitoring and observability setup
Framework-Specific Integration
Next.js App Router / Pages Router integration
React with context and hooks
Vue 3 with Composition API
Svelte/SvelteKit integration
Server-side rendering (SSR) with Supabase
Static site generation (SSG) patterns
Documentation Access & Search Strategy
Documentation Location Base Path: /Users/zach/Documents/cc-skills/docs/supabase/
Organized Structure
guides/auth/ - Authentication (30+ files)
guides/database/ - PostgreSQL, RLS, migrations, extensions (35+ files)
guides/storage/ - File storage and CDN
guides/realtime/ - Real-time subscriptions
guides/functions/ - Edge Functions (35+ files)
guides/ai/ - Vector embeddings and AI features (18+ files)
guides/cli/ - Supabase CLI and local development
guides/platform/ - Project management and deployment
guides/security/ - Security best practices
guides/deployment/ - Production deployment patterns
reference/ - Complete API reference (1,583 files)
Advanced Search Strategy When a request comes in, use a multi-stage search approach:
Stage 1: Broad Category Search
grep -r "keyword" /Users/zach/Documents/cc-skills/docs/supabase/guides/ -l | head -10
Stage 2: Targeted Deep Search
grep -r -B 2 -A 5 "specific pattern" /Users/zach/Documents/cc-skills/docs/supabase/guides/[category]/ --include="*.txt"
Stage 3: Cross-Reference Search
grep -r "related_term_1\|related_term_2\|related_term_3" /Users/zach/Documents/cc-skills/docs/supabase/ -l
Stage 4: API Reference Search
grep -r "method_name" /Users/zach/Documents/cc-skills/docs/supabase/reference/ -l
Documentation Reading Priority
Guides - For conceptual understanding and best practices
Reference - For specific API signatures and parameters
Cross-reference - Check related topics for complete context
Enhanced Process Framework
1. Deep Requirement Analysis Before providing any solution, analyze:
What Supabase feature is needed?
What's the user's framework/environment?
What's the scale/performance requirements?
What are the security considerations?
[ -f "next.config.js" ] && echo "Next.js detected"
[ -f "tsconfig.json" ] && echo "TypeScript project"
grep -r "createClient" . --include="*.{ts,js,tsx,jsx}" | head -5
Search for existing Supabase client setup
Identify current patterns being used
Check for potential conflicts or improvements
2. Comprehensive Documentation Search Execute multi-stage search strategy:
AUTH_DOCS=$(grep -r "authentication\|sign.*in\|auth\..*" /Users/zach/Documents/cc-skills/docs/supabase/guides/auth/ -l)
OAUTH_DOCS=$(echo "$AUTH_DOCS " | xargs grep -l "google\|oauth" )
3. Context-Aware Implementation Provide implementations that match the user's context:
Server Components patterns
Server Actions integration
Middleware for auth
Cookie-based session management
For Next.js Pages Router:
API routes patterns
getServerSideProps integration
Client-side auth hooks
Context providers
Custom hooks
Local state management
For Server-Side (Node/Deno/Bun):
Service role patterns
Connection pooling
Background jobs
4. Production-Grade Implementation Every code example should include:
✅ Complete TypeScript types
✅ Comprehensive error handling
✅ Loading states
✅ Edge cases handled
✅ Performance optimizations
✅ Security considerations
✅ Testing examples
✅ Monitoring/logging hooks
5. Validation & Testing Guidance
Unit test examples
Integration test examples
E2E test scenarios
RLS policy testing
Performance benchmarking
Security audit checklist
Advanced Implementation Patterns
Pattern Library Location .claude/skills/supabase-expert/patterns/
Pattern 1: Advanced RLS with Multi-Tenancy Scenario: Multi-tenant SaaS with organization-based access control
grep -r "multi.*tenant\|organization\|team.*access" /Users/zach/Documents/cc-skills/docs/supabase/guides/ -l
grep -r "row.*level.*security.*tenant" /Users/zach/Documents/cc-skills/docs/supabase/guides/database/ --include="*.txt" -A 10
CREATE TABLE organizations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE organization_members (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
role TEXT NOT NULL CHECK (role IN ('owner' , 'admin' , 'member' )),
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE (organization_id, user_id)
);
CREATE TABLE projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
name TEXT NOT NULL ,
created_by UUID REFERENCES auth.users(id),
created_at TIMESTAMPTZ DEFAULT NOW()
);
ALTER TABLE organizations ENABLE ROW LEVEL SECURITY;
ALTER TABLE organization_members ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
CREATE OR REPLACE FUNCTION is_organization_member(org_id UUID)
RETURNS BOOLEAN AS $$
BEGIN
RETURN EXISTS (
SELECT 1 FROM organization_members
WHERE organization_id = org_id
AND user_id = auth.uid()
);
END ;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE OR REPLACE FUNCTION get_user_role(org_id UUID)
RETURNS TEXT AS $$
BEGIN
RETURN (
SELECT role FROM organization_members
WHERE organization_id = org_id
AND user_id = auth.uid()
);
END ;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE POLICY "Users can view their organizations"
ON organizations FOR SELECT
USING (is_organization_member(id));
CREATE POLICY "Organization members can view projects"
ON projects FOR SELECT
USING (is_organization_member(organization_id));
CREATE POLICY "Admins and owners can insert projects"
ON projects FOR INSERT
WITH CHECK (
get_user_role(organization_id) IN ('admin' , 'owner' )
AND auth.uid() = created_by
);
CREATE POLICY "Admins and owners can update projects"
ON projects FOR UPDATE
USING (get_user_role(organization_id) IN ('admin' , 'owner' ));
CREATE POLICY "Owners can delete projects"
ON projects FOR DELETE
USING (get_user_role(organization_id) = 'owner' );
CREATE INDEX idx_org_members_user ON organization_members(user_id);
CREATE INDEX idx_org_members_org ON organization_members(organization_id);
CREATE INDEX idx_projects_org ON projects(organization_id);
export interface Organization {
id : string
name : string
created_at : string
}
export interface OrganizationMember {
id : string
organization_id : string
user_id : string
role : 'owner' | 'admin' | 'member'
created_at : string
}
export interface Project {
id : string
organization_id : string
name : string
created_by : string
created_at : string
}
import { createClient } from '@supabase/supabase-js'
import type { Database } from '@/types/supabase'
export class OrganizationService {
constructor (private supabase : ReturnType <typeof createClient<Database >> ) {}
async getOrganizations ( ) {
const { data, error } = await this .supabase
.from ('organizations' )
.select (`
*,
organization_members!inner(role)
` )
if (error) throw error
return data
}
async getProjects (organizationId : string ) {
const { data, error} = await this .supabase
.from ('projects' )
.select ('*' )
.eq ('organization_id' , organizationId)
.order ('created_at' , { ascending : false })
if (error) throw error
return data
}
async createProject (organizationId : string , name : string ) {
const { data : { user } } = await this .supabase .auth .getUser ()
if (!user) throw new Error ('Not authenticated' )
const { data, error } = await this .supabase
.from ('projects' )
.insert ({
organization_id : organizationId,
name,
created_by : user.id
})
.select ()
.single ()
if (error) throw error
return data
}
}
Pattern 2: Advanced Auth with Custom Claims grep -r "custom.*claims\|jwt.*metadata\|user.*metadata" /Users/zach/Documents/cc-skills/docs/supabase/guides/auth/ -l
grep -r "auth.*hooks\|hook.*send" /Users/zach/Documents/cc-skills/docs/supabase/guides/auth/ --include="*.txt" -A 10
import { createClient } from '@supabase/supabase-js'
export interface UserClaims {
role : 'admin' | 'user' | 'moderator'
organization_id ?: string
permissions : string []
}
export async function setUserClaims (
userId : string ,
claims : UserClaims
): Promise <void > {
const supabase = createClient (
process.env .SUPABASE_URL !,
process.env .SUPABASE_SERVICE_ROLE_KEY !
)
const { error } = await supabase.auth .admin .updateUserById (
userId,
{
app_metadata : { claims }
}
)
if (error) throw error
}
export async function getUserClaims (userId : string ): Promise <UserClaims | null > {
const supabase = createClient (
process.env .SUPABASE_URL !,
process.env .SUPABASE_SERVICE_ROLE_KEY !
)
const { data, error } = await supabase.auth .admin .getUserById (userId)
if (error) throw error
return data.user .app_metadata .claims as UserClaims || null
}
import { createServerClient } from '@supabase/ssr'
import { NextResponse , type NextRequest } from 'next/server'
export async function middleware (request : NextRequest ) {
const response = NextResponse .next ()
const supabase = createServerClient (
process.env .NEXT_PUBLIC_SUPABASE_URL !,
process.env .NEXT_PUBLIC_SUPABASE_ANON_KEY !,
{
cookies : {
get (name : string ) {
return request.cookies .get (name)?.value
},
set (name : string , value : string , options : any ) {
response.cookies .set ({ name, value, ...options })
},
remove (name : string , options : any ) {
response.cookies .set ({ name, value : '' , ...options })
},
},
}
)
const { data : { session } } = await supabase.auth .getSession ()
if (request.nextUrl .pathname .startsWith ('/admin' )) {
if (!session) {
return NextResponse .redirect (new URL ('/login' , request.url ))
}
const claims = session.user .app_metadata .claims as UserClaims
if (claims?.role !== 'admin' ) {
return NextResponse .redirect (new URL ('/unauthorized' , request.url ))
}
}
return response
}
export const config = {
matcher : ['/admin/:path*' , '/dashboard/:path*' ]
}
Pattern 3: Realtime with Presence and Broadcast grep -r "presence\|broadcast\|realtime.*channel" /Users/zach/Documents/cc-skills/docs/supabase/guides/realtime/ -l
import { useEffect, useState } from 'react'
import { createClient } from '@supabase/supabase-js'
import type { RealtimeChannel } from '@supabase/supabase-js'
interface PresenceState {
[key : string ]: {
user_id : string
username : string
online_at : string
}[]
}
export function usePresence (roomId : string ) {
const [presenceState, setPresenceState] = useState<PresenceState >({})
const [channel, setChannel] = useState<RealtimeChannel | null >(null )
useEffect (() => {
const supabase = createClient (
process.env .NEXT_PUBLIC_SUPABASE_URL !,
process.env .NEXT_PUBLIC_SUPABASE_ANON_KEY !
)
const presenceChannel = supabase.channel (`room:${roomId} ` , {
config : {
presence : {
key : 'user_id' ,
},
},
})
presenceChannel
.on ('presence' , { event : 'sync' }, () => {
const state = presenceChannel.presenceState ()
setPresenceState (state)
})
.on ('presence' , { event : 'join' }, ({ key, newPresences } ) => {
console .log ('User joined:' , key, newPresences)
})
.on ('presence' , { event : 'leave' }, ({ key, leftPresences } ) => {
console .log ('User left:' , key, leftPresences)
})
.subscribe (async (status) => {
if (status === 'SUBSCRIBED' ) {
const { data : { user } } = await supabase.auth .getUser ()
if (user) {
await presenceChannel.track ({
user_id : user.id ,
username : user.email ,
online_at : new Date ().toISOString (),
})
}
}
})
setChannel (presenceChannel)
return () => {
presenceChannel.unsubscribe ()
}
}, [roomId])
const sendBroadcast = async (event : string , payload : any ) => {
if (channel) {
await channel.send ({
type : 'broadcast' ,
event,
payload,
})
}
}
return {
presenceState,
onlineUsers : Object .values (presenceState).flat (),
sendBroadcast,
}
}
export function CollaborativeEditor ({ documentId }: { documentId: string } ) {
const { onlineUsers, sendBroadcast } = usePresence (documentId)
const handleCursorMove = (position : { x: number ; y: number } ) => {
sendBroadcast ('cursor_move' , position)
}
return (
<div >
<div className ="online-users" >
{onlineUsers.map(user => (
<div key ={user.user_id} >
{user.username} (online)
</div >
))}
</div >
{/* Editor component */}
</div >
)
}
Pattern 4: Vector Search with OpenAI Embeddings grep -r "vector\|embedding\|pgvector\|similarity" /Users/zach/Documents/cc-skills/docs/supabase/guides/ai/ -l
grep -r "semantic.*search\|vector.*search" /Users/zach/Documents/cc-skills/docs/supabase/guides/ai/ --include="*.txt" -A 10
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
content TEXT NOT NULL ,
embedding vector(1536 ),
metadata JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100 );
CREATE OR REPLACE FUNCTION match_documents(
query_embedding vector(1536 ),
match_threshold float ,
match_count int
)
RETURNS TABLE (
id UUID,
content TEXT,
metadata JSONB,
similarity float
) LANGUAGE sql STABLE AS $$
SELECT
id,
content,
metadata,
1 - (embedding <=> query_embedding) AS similarity
FROM documents
WHERE 1 - (embedding <=> query_embedding) > match_threshold
ORDER BY embedding <=> query_embedding
LIMIT match_count;
$$;
TypeScript Implementation:
import { createClient } from '@supabase/supabase-js'
import OpenAI from 'openai'
const openai = new OpenAI ({ apiKey : process.env .OPENAI_API_KEY })
const supabase = createClient (
process.env .SUPABASE_URL !,
process.env .SUPABASE_SERVICE_ROLE_KEY !
)
export async function generateEmbedding (text : string ): Promise <number []> {
const response = await openai.embeddings .create ({
model : 'text-embedding-ada-002' ,
input : text,
})
return response.data [0 ].embedding
}
export async function addDocument (
content : string ,
metadata : Record <string , any > = {}
): Promise <void > {
const embedding = await generateEmbedding (content)
const { error } = await supabase
.from ('documents' )
.insert ({
content,
embedding,
metadata,
})
if (error) throw error
}
export async function searchDocuments (
query : string ,
matchThreshold : number = 0.78 ,
matchCount : number = 10
) {
const queryEmbedding = await generateEmbedding (query)
const { data, error } = await supabase.rpc ('match_documents' , {
query_embedding : queryEmbedding,
match_threshold : matchThreshold,
match_count : matchCount,
})
if (error) throw error
return data
}
export async function ragQuery (userQuestion : string ): Promise <string > {
const relevantDocs = await searchDocuments (userQuestion, 0.78 , 5 )
const context = relevantDocs
.map (doc => doc.content )
.join ('\n\n' )
const response = await openai.chat .completions .create ({
model : 'gpt-4' ,
messages : [
{
role : 'system' ,
content : 'You are a helpful assistant. Answer questions based on the provided context.' ,
},
{
role : 'user' ,
content : `Context:\n${context} \n\nQuestion: ${userQuestion} ` ,
},
],
})
return response.choices [0 ].message .content || ''
}
Pattern 5: Edge Functions with Background Jobs grep -r "edge.*function\|deno\|background.*job" /Users/zach/Documents/cc-skills/docs/supabase/guides/functions/ -l
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
import Stripe from 'https://esm.sh/stripe@14.0.0'
const stripe = new Stripe (Deno .env .get ('STRIPE_SECRET_KEY' ) || '' , {
apiVersion : '2023-10-16' ,
})
const supabase = createClient (
Deno .env .get ('SUPABASE_URL' ) ?? '' ,
Deno .env .get ('SUPABASE_SERVICE_ROLE_KEY' ) ?? ''
)
interface PaymentRequest {
amount : number
currency : string
userId : string
organizationId : string
}
serve (async (req) => {
try {
const authHeader = req.headers .get ('Authorization' )
if (!authHeader) {
return new Response (
JSON .stringify ({ error : 'Missing authorization' }),
{ status : 401 }
)
}
const { data : { user }, error : authError } = await supabase.auth .getUser (
authHeader.replace ('Bearer ' , '' )
)
if (authError || !user) {
return new Response (
JSON .stringify ({ error : 'Invalid authorization' }),
{ status : 401 }
)
}
const body : PaymentRequest = await req.json ()
const paymentIntent = await stripe.paymentIntents .create ({
amount : body.amount ,
currency : body.currency ,
metadata : {
user_id : body.userId ,
organization_id : body.organizationId ,
},
})
const { error : dbError } = await supabase
.from ('payments' )
.insert ({
user_id : body.userId ,
organization_id : body.organizationId ,
stripe_payment_intent_id : paymentIntent.id ,
amount : body.amount ,
currency : body.currency ,
status : 'pending' ,
})
if (dbError) throw dbError
return new Response (
JSON .stringify ({
clientSecret : paymentIntent.client_secret ,
paymentIntentId : paymentIntent.id ,
}),
{
headers : { 'Content-Type' : 'application/json' },
status : 200 ,
}
)
} catch (error) {
return new Response (
JSON .stringify ({ error : error.message }),
{
headers : { 'Content-Type' : 'application/json' },
status : 500 ,
}
)
}
})
Framework-Specific Implementations
Next.js App Router (Complete Setup)
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'
import type { Database } from '@/types/supabase'
export function createClient ( ) {
const cookieStore = cookies ()
return createServerClient<Database >(
process.env .NEXT_PUBLIC_SUPABASE_URL !,
process.env .NEXT_PUBLIC_SUPABASE_ANON_KEY !,
{
cookies : {
get (name : string ) {
return cookieStore.get (name)?.value
},
set (name : string , value : string , options : CookieOptions ) {
try {
cookieStore.set ({ name, value, ...options })
} catch (error) {
}
},
remove (name : string , options : CookieOptions ) {
try {
cookieStore.set ({ name, value : '' , ...options })
} catch (error) {
}
},
},
}
)
}
import { createBrowserClient } from '@supabase/ssr'
import type { Database } from '@/types/supabase'
export function createClient ( ) {
return createBrowserClient<Database >(
process.env .NEXT_PUBLIC_SUPABASE_URL !,
process.env .NEXT_PUBLIC_SUPABASE_ANON_KEY !
)
}
import { createClient } from '@/lib/supabase/server'
import { NextResponse } from 'next/server'
export async function GET (request : Request ) {
const { searchParams, origin } = new URL (request.url )
const code = searchParams.get ('code' )
const next = searchParams.get ('next' ) ?? '/dashboard'
if (code) {
const supabase = createClient ()
const { error } = await supabase.auth .exchangeCodeForSession (code)
if (!error) {
return NextResponse .redirect (`${origin} ${next} ` )
}
}
return NextResponse .redirect (`${origin} /auth/auth-code-error` )
}
import { createClient } from '@/lib/supabase/server'
import { redirect } from 'next/navigation'
export default async function DashboardPage ( ) {
const supabase = createClient ()
const {
data : { user },
} = await supabase.auth .getUser ()
if (!user) {
redirect ('/login' )
}
const { data : projects } = await supabase
.from ('projects' )
.select ('*' )
.order ('created_at' , { ascending : false })
return (
<div >
<h1 > Welcome {user.email}</h1 >
<ProjectsList projects ={projects || []} />
</div >
)
}
'use server'
import { createClient } from '@/lib/supabase/server'
import { revalidatePath } from 'next/cache'
export async function createProject (formData : FormData ) {
const supabase = createClient ()
const {
data : { user },
} = await supabase.auth .getUser ()
if (!user) {
return { error : 'Not authenticated' }
}
const name = formData.get ('name' ) as string
const organizationId = formData.get ('organizationId' ) as string
const { data, error } = await supabase
.from ('projects' )
.insert ({
name,
organization_id : organizationId,
created_by : user.id ,
})
.select ()
.single ()
if (error) {
return { error : error.message }
}
revalidatePath ('/dashboard' )
return { data }
}
export async function deleteProject (projectId : string ) {
const supabase = createClient ()
const { error } = await supabase
.from ('projects' )
.delete ()
.eq ('id' , projectId)
if (error) {
return { error : error.message }
}
revalidatePath ('/dashboard' )
return { success : true }
}
Performance Optimization Strategies
Strategy 1: Connection Pooling for Serverless grep -r "connection.*pool\|supavisor\|serverless" /Users/zach/Documents/cc-skills/docs/supabase/guides/ -l
import { createClient } from '@supabase/supabase-js'
const POOLER_URL = process.env .SUPABASE_URL ?.replace (
'.supabase.co' ,
'.pooler.supabase.com'
)
export function createPooledClient ( ) {
return createClient (
POOLER_URL || process.env .SUPABASE_URL !,
process.env .SUPABASE_SERVICE_ROLE_KEY !,
{
db : {
schema : 'public' ,
},
auth : {
persistSession : false ,
},
}
)
}
export async function GET (request : Request ) {
const supabase = createPooledClient ()
const { data, error } = await supabase
.from ('large_table' )
.select ('*' )
.limit (1000 )
return Response .json ({ data, error })
}
Strategy 2: Query Optimization
const { data } = await supabase
.from ('users' )
.select ('*' )
const { data } = await supabase
.from ('users' )
.select ('id, email, username' )
const { data, error, count } = await supabase
.from ('users' )
.select ('*' , { count : 'exact' })
.range (0 , 49 )
.order ('created_at' , { ascending : false })
const { data } = await supabase
.from ('posts' )
.select (`
id,
title,
author:users!inner(id, username),
comments(count)
` )
.eq ('published' , true )
.limit (10 )
const { data } = await supabase
.from ('posts' )
.select ('*' )
.eq ('user_id' , userId)
.eq ('status' , 'published' )
Strategy 3: Caching Layer
import { createClient } from '@supabase/supabase-js'
import { unstable_cache } from 'next/cache'
const supabase = createClient (
process.env .SUPABASE_URL !,
process.env .SUPABASE_ANON_KEY !
)
export const getCachedProjects = unstable_cache (
async (organizationId : string ) => {
const { data, error } = await supabase
.from ('projects' )
.select ('*' )
.eq ('organization_id' , organizationId)
.order ('created_at' , { ascending : false })
if (error) throw error
return data
},
['projects' ],
{
revalidate : 60 ,
tags : ['projects' ],
}
)
import { revalidateTag } from 'next/cache'
export async function createProject (name : string , orgId : string ) {
const { data, error } = await supabase
.from ('projects' )
.insert ({ name, organization_id : orgId })
.select ()
.single ()
if (!error) {
revalidateTag ('projects' )
}
return { data, error }
}
Security Audit Checklist
Critical Security Checks
interface SecurityAudit {
checks : SecurityCheck []
passed : boolean
failures : string []
}
interface SecurityCheck {
name : string
passed : boolean
message ?: string
}
export async function auditSupabaseSecurity ( ): Promise <SecurityAudit > {
const checks : SecurityCheck [] = []
const serviceKeyCheck = !process.env .NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY
checks.push ({
name : 'Service Role Key Security' ,
passed : serviceKeyCheck,
message : serviceKeyCheck
? 'Service role key not exposed in public env vars'
: '❌ CRITICAL: Service role key exposed in public env vars!' ,
})
const supabase = createClient (
process.env .SUPABASE_URL !,
process.env .SUPABASE_SERVICE_ROLE_KEY !
)
const { data : tables } = await supabase.rpc ('check_rls_enabled' )
const rlsCheck = tables?.every (t => t.rls_enabled )
checks.push ({
name : 'RLS Enabled on All Tables' ,
passed : rlsCheck || false ,
message : rlsCheck
? 'All tables have RLS enabled'
: '⚠️ Some tables missing RLS policies' ,
})
const sqlInjectionCheck = true
checks.push ({
name : 'No SQL Injection Risks' ,
passed : sqlInjectionCheck,
})
const httpsCheck = process.env .SUPABASE_URL ?.startsWith ('https://' )
checks.push ({
name : 'HTTPS Only' ,
passed : httpsCheck || false ,
message : httpsCheck ? 'Using HTTPS' : '❌ Not using HTTPS!' ,
})
const passed = checks.every (check => check.passed )
const failures = checks
.filter (check => !check.passed )
.map (check => check.message || check.name )
return { checks, passed, failures }
}
Testing Utilities
RLS Policy Testing
import { createClient } from '@supabase/supabase-js'
import { describe, it, expect, beforeAll } from 'vitest'
describe ('RLS Policies' , () => {
let supabase : ReturnType <typeof createClient>
let testUserId : string
beforeAll (async () => {
supabase = createClient (
process.env .SUPABASE_URL !,
process.env .SUPABASE_SERVICE_ROLE_KEY !
)
const { data : { user } } = await supabase.auth .admin .createUser ({
email : 'test@example.com' ,
password : 'test-password-123' ,
email_confirm : true ,
})
testUserId = user!.id
})
it ('should allow users to read their own data' , async () => {
const { data : { session } } = await supabase.auth .signInWithPassword ({
email : 'test@example.com' ,
password : 'test-password-123' ,
})
const userClient = createClient (
process.env .SUPABASE_URL !,
process.env .SUPABASE_ANON_KEY !,
{
global : {
headers : {
Authorization : `Bearer ${session!.access_token} ` ,
},
},
}
)
const { data, error } = await userClient
.from ('users' )
.select ('*' )
.eq ('id' , testUserId)
expect (error).toBeNull ()
expect (data).toHaveLength (1 )
})
it ('should prevent users from reading other users data' , async () => {
const { data : { session } } = await supabase.auth .signInWithPassword ({
email : 'test@example.com' ,
password : 'test-password-123' ,
})
const userClient = createClient (
process.env .SUPABASE_URL !,
process.env .SUPABASE_ANON_KEY !,
{
global : {
headers : {
Authorization : `Bearer ${session!.access_token} ` ,
},
},
}
)
const { data, error } = await userClient
.from ('users' )
.select ('*' )
.neq ('id' , testUserId)
expect (data).toHaveLength (0 )
})
})
Error Handling Framework
Comprehensive Error Handler
import { PostgrestError } from '@supabase/supabase-js'
export class SupabaseError extends Error {
constructor (
public code : string ,
public details : string ,
public hint ?: string
) {
super (details)
this .name = 'SupabaseError'
}
}
export function handleSupabaseError (error : PostgrestError | null ): never {
if (!error) {
throw new Error ('Unknown error occurred' )
}
if (error.code === '42501' || error.message .includes ('policy' )) {
throw new SupabaseError (
'RLS_POLICY_VIOLATION' ,
'You do not have permission to perform this action' ,
'Check row-level security policies'
)
}
if (error.code === '23505' ) {
const field = error.message .match (/Key \((.*?)\)/ )?.[1 ]
throw new SupabaseError (
'DUPLICATE_ENTRY' ,
`A record with this ${field} already exists` ,
'Use a different value or update the existing record'
)
}
if (error.code === '23503' ) {
throw new SupabaseError (
'INVALID_REFERENCE' ,
'The referenced record does not exist' ,
'Ensure the related record exists before creating this one'
)
}
if (error.message .includes ('Failed to fetch' )) {
throw new SupabaseError (
'CONNECTION_ERROR' ,
'Unable to connect to the database' ,
'Check your network connection and Supabase status'
)
}
throw new SupabaseError (
error.code || 'UNKNOWN_ERROR' ,
error.message ,
error.hint
)
}
try {
const { data, error } = await supabase
.from ('users' )
.insert ({ email : 'test@example.com' })
if (error) handleSupabaseError (error)
return data
} catch (err) {
if (err instanceof SupabaseError ) {
console .error (`[${err.code} ] ${err.details} ` )
if (err.hint ) console .error (`Hint: ${err.hint} ` )
return {
error : true ,
message : err.details ,
code : err.code ,
}
}
throw err
}
Monitoring & Observability
import { createClient } from '@supabase/supabase-js'
interface QueryLog {
query : string
duration : number
error ?: string
timestamp : string
}
export class SupabaseMonitor {
private logs : QueryLog [] = []
constructor (private supabase : ReturnType <typeof createClient> ) {
this .wrapClient ()
}
private wrapClient ( ) {
const originalFrom = this .supabase .from .bind (this .supabase )
this .supabase .from = (table : string ) => {
const startTime = Date .now ()
const builder = originalFrom (table)
const wrapMethod = (method : string ) => {
const original = (builder as any )[method].bind (builder)
;(builder as any )[method] = async (...args : any []) => {
const result = await original (...args)
const duration = Date .now () - startTime
this .logs .push ({
query : `${method} on ${table} ` ,
duration,
error : result.error ?.message ,
timestamp : new Date ().toISOString (),
})
if (duration > 1000 ) {
console .warn (`Slow query detected: ${method} on ${table} took ${duration} ms` )
}
return result
}
}
;['select' , 'insert' , 'update' , 'delete' , 'upsert' ].forEach (wrapMethod)
return builder
}
}
getLogs ( ) {
return this .logs
}
getSlowQueries (threshold = 1000 ) {
return this .logs .filter (log => log.duration > threshold)
}
getErrorRate ( ) {
const totalQueries = this .logs .length
const errorQueries = this .logs .filter (log => log.error ).length
return totalQueries > 0 ? errorQueries / totalQueries : 0
}
}
Migration Utilities
Migration from Firebase
import admin from 'firebase-admin'
import { createClient } from '@supabase/supabase-js'
const supabase = createClient (
process.env .SUPABASE_URL !,
process.env .SUPABASE_SERVICE_ROLE_KEY !
)
admin.initializeApp ({
credential : admin.credential .cert ('./firebase-credentials.json' ),
})
export async function migrateUsers ( ) {
const auth = admin.auth ()
let nextPageToken : string | undefined
do {
const listUsersResult = await auth.listUsers (1000 , nextPageToken)
for (const userRecord of listUsersResult.users ) {
try {
const { data, error } = await supabase.auth .admin .createUser ({
email : userRecord.email !,
email_confirm : true ,
user_metadata : {
name : userRecord.displayName ,
avatar_url : userRecord.photoURL ,
migrated_from_firebase : true ,
},
})
if (error) {
console .error (`Failed to migrate user ${userRecord.email} :` , error)
continue
}
console .log (`Migrated user: ${userRecord.email} ` )
} catch (err) {
console .error (`Error migrating user ${userRecord.email} :` , err)
}
}
nextPageToken = listUsersResult.pageToken
} while (nextPageToken)
}
export async function migrateFirestoreCollection (
collectionName : string ,
tableName : string
) {
const firestore = admin.firestore ()
const snapshot = await firestore.collection (collectionName).get ()
for (const doc of snapshot.docs ) {
const data = doc.data ()
const row = {
id : doc.id ,
...data,
created_at : data.createdAt ?._seconds
? new Date (data.createdAt ._seconds * 1000 ).toISOString ()
: null ,
}
const { error } = await supabase
.from (tableName)
.insert (row)
if (error) {
console .error (`Failed to migrate document ${doc.id} :` , error)
} else {
console .log (`Migrated document: ${doc.id} ` )
}
}
}
Output Format When providing Supabase guidance, follow this comprehensive format:
1. Deep Analysis
Understand user's context (framework, scale, requirements)
Identify potential challenges and edge cases
Consider security implications
2. Documentation Research
Cite specific documentation files consulted
Reference API documentation for exact signatures
Cross-reference related features
3. Production-Grade Implementation
Complete TypeScript code with all types
Comprehensive error handling
Loading states and edge cases
Performance optimizations built-in
Security best practices applied
Monitoring/logging hooks
4. Testing Strategy
Unit test examples
Integration test scenarios
RLS policy testing
E2E test guidance
5. Deployment Guidance
Environment variable setup
Migration scripts
Rollback procedures
Monitoring setup
6. Performance Considerations
Query optimization tips
Caching strategies
Connection pooling guidance
Index recommendations
7. Security Review
RLS policy review
Input validation
API key security
CORS configuration
8. Next Steps & Scaling
Related features to implement
Scaling considerations
Advanced patterns to explore
Notes
Always search documentation first - Consult 2,190 pages before answering
PostgreSQL expertise required - Supabase is PostgreSQL, apply PG best practices
Deno for Edge Functions - Not Node.js, different module system
RLS is mandatory - Test thoroughly, security is critical
Type generation is essential - Always generate types from schema
Connection pooling - Required for serverless/Edge deployments
Service role = superuser - Never expose to clients
Anon key is safe - Can be used in client-side code
Local development - Requires Docker for Supabase CLI
Test RLS exhaustively - Use multiple user contexts
Documentation Search Shortcuts
alias sb-auth="grep -r '$1 ' /Users/zach/Documents/cc-skills/docs/supabase/guides/auth/ -l"
alias sb-db="grep -r '$1 ' /Users/zach/Documents/cc-skills/docs/supabase/guides/database/ -l"
alias sb-storage="grep -r '$1 ' /Users/zach/Documents/cc-skills/docs/supabase/guides/storage/ -l"
alias sb-realtime="grep -r '$1 ' /Users/zach/Documents/cc-skills/docs/supabase/guides/realtime/ -l"
alias sb-functions="grep -r '$1 ' /Users/zach/Documents/cc-skills/docs/supabase/guides/functions/ -l"
alias sb-ai="grep -r '$1 ' /Users/zach/Documents/cc-skills/docs/supabase/guides/ai/ -l"
alias sb-api="grep -r '$1 ' /Users/zach/Documents/cc-skills/docs/supabase/reference/ -l | head -20"
Quick Reference Commands
supabase init
supabase start
supabase status
supabase stop
supabase migration new <name>
supabase db reset
supabase db push
supabase db pull
supabase db diff
supabase gen types typescript --local
supabase gen types typescript --project-id <id >
supabase functions new <name>
supabase functions serve
supabase functions deploy <name>
supabase functions logs <name>
supabase auth users list
supabase auth users get <user-id>
supabase secrets set SECRET_NAME=value
supabase secrets list