Standards Docs
Guides

Supabase Setup

Initialize migrations, apply them, and configure RLS for tenant isolation with @stndrds/adapter-supabase.

@stndrds/adapter-supabase provides the database adapter and typed repository layer. This guide covers initializing migrations, applying them, and configuring RLS for tenant isolation.

Initialize

standards requires several core tables: actors, actor_roles, record_reference_edges, records, object_definitions, and more. These are all defined in the baseline migration shipped with standards-platform.

@stndrds/adapter-supabase does not expose its own CLI or migration runner. Migrations live in your project's supabase/migrations/ directory (following the Supabase convention) and are applied with the Supabase CLI.

Copy the baseline migration from standards-platform to get started:

# In your project root (requires supabase CLI)
cp path/to/standards-platform/supabase/migrations/20260516000000_stndrds_initial_schema.sql \
  supabase/migrations/

The @stndrds/adapter-supabase package exports types, repository helpers, and filter utilities — not a migration runner. All schema setup is done via the Supabase CLI.

Migration commands

Use the Supabase CLI directly. The scripts/setup.sh in standards-platform shows the full initialization sequence:

# Start local Supabase (Docker)
pnpm exec supabase start

# Apply all pending migrations to local DB
pnpm exec supabase db push

# Reset and re-apply from scratch (dev only)
pnpm exec supabase db reset

For production, run supabase db push against your hosted project:

supabase db push --db-url postgresql://postgres:<password>@<host>:5432/postgres

There is no @stndrds/adapter-supabase migrate CLI command. The adapter is a pure runtime library.

Keep migrations at parity with the runtime. Record-reference writes now run inside dedicated Postgres RPCs for atomicity (single-record create and delete each in one transaction, plus set-based bulk-create and bulk-delete). The non-atomic per-attribute edge-sync fallback has been removed: if the sync_record_reference_edges RPC is missing (Postgres returns PGRST202), the write throws a ConfigurationError instead of silently degrading. Apply the migration that ships these RPCs before writing records with relations.

RLS policies

standards relies on Postgres RLS for tenant isolation. Every core table has a tenant_isolation policy using current_setting('app.tenant_id', true). The adapter sets this session variable before each query.

The production pattern from standards-platform's migrations:

alter table public.records enable row level security;

create policy records_tenant_isolation
  on public.records
  using (tenant_id = (current_setting('app.tenant_id', true))::uuid)
  with check (tenant_id = (current_setting('app.tenant_id', true))::uuid);

Apply the same pattern to any custom tables you add. The with check clause enforces the tenant on writes; using enforces it on reads.

For the full set of production policies, refer to standards-platform/supabase/migrations/20260516000000_stndrds_initial_schema.sql.

Always run migrations with the service-role key. RLS does not apply to migration scripts — the service role bypasses RLS by design, which is required to create policies and seed data.

Generated types

Supabase generates a Database type from your schema. Pass it to SupabaseDatabaseAdapter for end-to-end type safety:

// @noverify
import type { Database } from "@my-app/supabase";
import { SupabaseDatabaseAdapter } from "@stndrds/adapter-supabase";
import { MeilisearchSearchAdapter } from "@stndrds/adapter-meilisearch";

const search = new MeilisearchSearchAdapter({
  host: process.env.MEILISEARCH_HOST ?? "",
  apiKey: process.env.MEILISEARCH_API_KEY ?? "",
  indexPrefix: "myapp",
});

const adapter = new SupabaseDatabaseAdapter<Database>(supabase, { search });

Generate types with the Supabase CLI whenever you add a migration:

supabase gen types typescript --local > src/types/database.types.ts

The search option in SupabaseDatabaseAdapter is required when you use Meilisearch. Omit it only if you disable search entirely in SchemaModule.forRoot.

On this page