supabase-database
SKILL.md
Supabase Database
Quick Start
When working with Supabase:
- Always use Supabase client from
@/lib/supabase/serveror@/lib/supabase/client - Never make queries without filtering by
tenant_idin multitenant tables - Always enable RLS on tables and include tenant verification in policies
- Use prepared parameters (Supabase client methods, never string concatenation)
- Use migrations in
supabase/migrations/for schema changes
Key Files
src/lib/supabase/- Supabase client utilitiessrc/lib/integrations/supabase/- Supabase integrationsupabase/migrations/- Database migrationssupabase/functions/- Edge functionssrc/lib/auth/enterprise-rls-utils.ts- RLS utilities
Common Patterns
Basic Query with Tenant
import { createClient } from '@/lib/supabase/server';
import { getTenantFromRequest } from '@/lib/tenant/tenant-service';
const supabase = createClient();
const tenant = await getTenantFromRequest(request);
const { data, error } = await supabase
.from('products')
.select('id, name, price, category_id')
.eq('tenant_id', tenant.id)
.eq('active', true)
.order('created_at', { ascending: false })
.limit(20);
if (error) {
console.error('Query error:', error);
return NextResponse.json({ error: error.message }, { status: 500 });
}
Insert with Tenant
const { data, error } = await supabase
.from('products')
.insert({
name: 'Pintura Blanca',
price: 5000,
tenant_id: tenant.id,
category_id: categoryId,
active: true,
})
.select()
.single();
Update with Tenant
const { data, error } = await supabase
.from('products')
.update({ price: 5500 })
.eq('id', productId)
.eq('tenant_id', tenant.id)
.select()
.single();
RLS Policy Example
-- Enable RLS
ALTER TABLE products ENABLE ROW LEVEL SECURITY;
-- Policy for tenant isolation
CREATE POLICY "tenant_isolation_products" ON products
FOR ALL
USING (
tenant_id = (
SELECT id FROM tenants
WHERE slug = current_setting('app.tenant_slug', true)
)
);
-- Policy for public read (if needed)
CREATE POLICY "public_read_products" ON products
FOR SELECT
USING (active = true);
Using RLS Utils
import { executeWithRLS } from '@/lib/auth/enterprise-rls-utils';
const result = await executeWithRLS(
enterpriseContext,
async (client, rlsContext) => {
return await client
.from('products')
.select('*')
.eq('tenant_id', rlsContext.tenantId);
}
);
Migration Template
-- Migration: add_new_column_to_products
-- Created: 2026-01-23
BEGIN;
-- Add new column
ALTER TABLE products
ADD COLUMN IF NOT EXISTS new_field VARCHAR(255);
-- Create index if needed
CREATE INDEX IF NOT EXISTS idx_products_new_field
ON products(new_field)
WHERE new_field IS NOT NULL;
-- Update RLS policy if needed
-- (Add to existing policy or create new one)
COMMIT;
Best Practices
- Always filter by tenant_id in multitenant tables
- Use
.select()with specific fields instead of* - Enable RLS on all tables
- Use migrations for all schema changes
- Test migrations in development first
- Use transactions for multiple related operations
- Index frequently queried fields (tenant_id, foreign keys)
Commands
# Create new migration
supabase migration new migration_name
# Apply migrations
supabase db push
# Reset database (development)
supabase db reset
# Generate TypeScript types
supabase gen types typescript --local > src/types/database.ts
Weekly Installs
9
Repository
santiagoxor/pin…-digitalFirst Seen
Feb 28, 2026
Security Audits
Installed on
opencode9
gemini-cli9
github-copilot9
codex9
amp9
cline9