Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ency07/B2B/llms.txt

Use this file to discover all available pages before exploring further.

Database schema changes are managed as SQL migration files in supabase/migrations/ with UTC timestamps as filenames. There are no automatic down migrations — rollbacks are executed manually with reverse SQL statements. Every migration file is reviewed before it reaches production.

File naming convention

Migration files follow the format YYYYMMDDHHMMSS_description.sql:
supabase/migrations/
  20260621000038_public_rls_policies.sql
  20260622000039_rls_authenticated_policies.sql
The timestamp prefix guarantees deterministic ordering and prevents filename collisions across parallel branches.

Applying migrations

1

Create the migration file

Add a new .sql file in supabase/migrations/ using the next UTC timestamp as the filename prefix:
touch supabase/migrations/$(date -u +%Y%m%d%H%M%S)_your_description.sql
Write your CREATE TABLE, ALTER TABLE, CREATE INDEX, and CREATE POLICY statements inside the file.
2

Point .env.local at the correct project

Verify that .env.local contains the Supabase URL and service-role key for the target environment (local, staging, or production) before executing the migration script.
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...
3

Run the migration script

npx ts-node scripts/deploy-migrations.ts
4

Verify RLS coverage

After the migration, run the RLS coverage checker to confirm every new table has Row Level Security enabled and policies defined:
npx ts-node scripts/check-rls-coverage.ts

New table checklist

Every new table must satisfy this checklist before its migration file is committed:
1

Create the table

CREATE TABLE public.my_table (
  id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id   uuid NOT NULL REFERENCES public.tenants(id),
  -- ... other columns
  created_at  timestamptz NOT NULL DEFAULT now(),
  updated_at  timestamptz,
  deleted_at  timestamptz,
  created_by  uuid REFERENCES auth.users(id)
);
2

Enable Row Level Security

ALTER TABLE public.my_table ENABLE ROW LEVEL SECURITY;
RLS must be enabled on every table in the public schema, including lookup/reference tables.
3

Create RLS policies

Create policies for the roles that need access. Always scope data access by tenant_id:
-- Authenticated users: access their own tenant's rows only
CREATE POLICY "my_table_authenticated_select"
  ON public.my_table FOR SELECT
  TO authenticated
  USING (tenant_id = (SELECT tenant_id FROM public.user_profiles WHERE id = auth.uid()));

-- If public/anonymous access is needed (use sparingly)
CREATE POLICY "my_table_anon_select"
  ON public.my_table FOR SELECT
  TO anon
  USING (is_public = true);
4

Add traceability triggers

Attach updated_at and deleted_at triggers so every row change is auditable:
-- Auto-update updated_at on row modification
CREATE TRIGGER set_updated_at
  BEFORE UPDATE ON public.my_table
  FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();

Migration environments

EnvironmentCommand.env target
Developmentnpx ts-node scripts/deploy-migrations.ts.env.local → local Supabase
Stagingnpx ts-node scripts/deploy-migrations.ts.env.local → staging project
ProductionManual SQL review + deploy-migrations.ts.env.local → prod project
Each environment must point to a separate Supabase project to avoid accidental data cross-contamination. Use npx ts-node scripts/setup-env.ts <env> to quickly switch .env.local between targets.
Always review migration SQL manually before running against production. The project does not use down migrations — an incorrect migration requires a hand-written rollback SQL statement. There is no automated undo path.

Rollback SQL reference

Use these patterns to reverse common migration types directly in the Supabase Dashboard SQL Editor:
-- Simple table rollback
DROP TABLE IF EXISTS public.mi_nueva_tabla CASCADE;

-- Column rollback
ALTER TABLE public.mi_tabla DROP COLUMN IF EXISTS mi_columna;

-- Policy rollback
DROP POLICY IF EXISTS mi_policy ON public.mi_tabla;

-- Index rollback
DROP INDEX IF EXISTS idx_mi_tabla_mi_columna;

Production checklist

Run through this checklist for every migration that targets the production Supabase project:
1

Take a manual backup

pg_dump --dbname="$SUPABASE_DATABASE_URL" \
  --schema=public \
  --exclude-table-data='audit_logs' \
  --file=backup_$(date +%Y%m%d).sql
Store the backup file in a secure, off-database location (e.g. an S3 bucket or encrypted volume) with at least 30 days retention.
2

Get a second-engineer review

Open a PR or send the migration SQL to a second engineer. Confirm: no destructive changes to existing data, correct RLS policies, and all foreign keys reference valid tables.
3

Apply to staging first

Point .env.local at the staging project and run:
npx ts-node scripts/deploy-migrations.ts
Verify the staging app works end-to-end before proceeding.
4

Verify staging health

npx ts-node scripts/check-rls-coverage.ts && npx vitest run
All tests must pass and RLS coverage must be 100 %.
5

Apply to production during a low-traffic window

Switch .env.local to the production project and run the migration script. Monitor Supabase logs and the /api/health?full=true endpoint for the 10 minutes following the migration.

Build docs developers (and LLMs) love