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.

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