Skip to content

Database and REST

Put durable schema changes in database.migrations and browser access in database.expose in your release manifest. Start with the complete first-deploy example, then change the policy to match the app’s intended readers and writers.

Terminal window
run402 up --check
run402 up --plan --project prj_example
run402 up --project prj_example
run402 projects sql prj_example "SELECT id, title FROM items LIMIT 10"

A migration ID identifies a particular SQL body. Reusing the ID with a different checksum fails; add a new migration. Inspect destructive changes and rehearsal results before applying them to a live database.

Tables are dark until exposed. TABLE_NOT_EXPOSED means the exposure declaration needs attention; it does not mean “add an RLS policy” blindly. On an exposed table, native PostgREST permission errors can still occur. REST_PERMISSION_DENIED diagnostics do not prove a particular RLS cause.

Browser code uses public project config and the user’s session; server code normally uses caller-scoped db(req) or db() in the supported request context. adminDb() bypasses row policies and belongs only in explicitly privileged server operations. Never solve a denied browser request by shipping a service key.

See REST and frontend examples and authentication for native application code. CLI is for operating the database; the deployed app still makes runtime data requests.

Use public_read_append_only for public contribution tables: readers may insert new rows but cannot edit or delete existing rows. Constraints still validate submissions. Custom policies receive the scoped table and owned-sequence privileges they require during server apply; do not duplicate them with GRANT statements. Plan and validation responses distinguish pending access previews from PostgreSQL structural checks, and row predicates remain runtime checks.