Route group: /p/:id/database/* (index redirects to functions). Source: apps/studio/src/features/database.
Functions
GET /admin/schema/functions and friends via functions.api.ts. Lists Postgres functions with their return types, and is the source for the function picker used by the trigger editor.
Triggers
triggers.api.ts manages real Postgres triggers (CREATE TRIGGER … EXECUTE FUNCTION …). TriggerEditorModal composes table and function references (TriggerTableRef, TriggerFnRef) into a statement. Full semantics: Postgres triggers.
Extensions
extensions.api.ts lists installed and available extensions and installs one — e.g. POST /admin/schema/extensions/pg_cron, which the cron screen calls when the extension is missing.
Publications
/p/:id/database/publications, GET/POST /admin/database/publications, PATCH/DELETE /admin/database/publications/:name.
A publication is a list: these tables (or every table), these kinds of change (insert, update, delete,
truncate). It sends nothing by itself. A subscriber (another Postgres, Debezium, a warehouse loader) connects to
the database over a replication connection, names the publication, and from then on receives every change it covers
as it commits. New publication takes the name, the changes and the tables in one step. Each publication's
Connect button shows the subscriber side: whether Postgres is reachable from outside (and where to publish a
port if not, under Project Settings → Database access), the role, and a ready
CREATE SUBSCRIPTION … PUBLICATION <name> statement. A banner warns when wal_level is not logical, in which case publications are defined but stream
nothing until Postgres restarts with it.
Publications do not drive Motherbase Realtime: which tables broadcast is chosen under Realtime.
Column privileges
/p/:id/database/column-privileges, GET/PUT /admin/database/column-privileges.
RLS policies decide which rows a role reaches; column privileges decide which columns of those rows. Pick a table and
a role (anon = no signed-in user, authenticated = a signed-in user, service_role = the secret key, which skips
RLS but not these grants), then uncheck what the role must not touch. SELECT reads the column, INSERT gives it a
value on a new row, UPDATE changes it, REFERENCES lets a foreign key point at it. The page lists the columns the
role cannot read. When the role holds a privilege on the whole table, unchecking one column replaces that grant with
one on every other column, the only way Postgres can hide a single column.
Postgres enforces it: a REST request that names a column the role may not read is refused, and so is select=*,
which expands to every column. Clients of a table with hidden columns select the columns they need.
Settings
/p/:id/database/settings:
- Connection: how the API reaches Postgres and whether a port is published.
- SQL editor (
database.sqlEditor): whether the SQL editor may write. On by default; off, every statement runs in aREAD ONLYtransaction. See SQL editor. - Configuration: the Postgres parameters worth tuning,
GET/PATCH /admin/database/config, the same list asmotherbase postgres-config.
Migrations
Studio-authored, reviewed migrations backed by the SchemaMigration table. Migrations can also
arrive from a connected GitHub repository — see Git Migrations.
| Action | Endpoint |
|---|---|
| List | GET /admin/migrations |
| Detail | GET /admin/migrations/:id |
| Diff against live schema | GET /admin/migrations/diff |
| Create | POST /admin/migrations with { name, upSql, downSql? } |
| Apply | POST /admin/migrations/:id/apply |
| Roll back | POST /admin/migrations/:id/rollback |
NewMigrationModal drafts the SQL, MigrationDiffModal shows drift, MigrationDetailModal shows the stored statements, MigrationStatusBadge renders pending / applied / rolledback. A checksum of upSql detects edits made after creation. See the data model.
An empty downSql is sent as undefined rather than an empty string — a migration is either reversible or explicitly not.
Backups
| Action | Endpoint |
|---|---|
| List | GET /admin/backups/list |
| Read schedule config | GET /admin/backups/config |
| Save schedule config | PUT /admin/backups/config |
| Run now | POST /admin/backups/run |
| Restore | POST /admin/backups/restore |
Restore takes either a local file ({ file }) or an offsite object ({ key }), plus confirm: 'RESTORE'. RestoreModal is what collects that confirmation.
Restoring replaces the project's database contents. The confirmation string is required by the API itself, not just the UI, so an accidental API call cannot trigger it.
Schedule, retention and offsite settings live in the backup.schedule settings group; see backups and restore.
Webhooks
webhooks.api.ts manages Webhook rows and their events[], and can send a test delivery returning { ok, status, ms }. WebhookDrawer shows recent WebhookDelivery rows. Matching rules and payloads: Events & webhooks.
Cron
Two kinds of recurring work on one screen:
| Kind | Endpoints |
|---|---|
pg_cron jobs | GET /admin/cron/pg/jobs, POST /admin/cron/pg/jobs, PATCH /admin/cron/pg/jobs/:jobid (active), DELETE /admin/cron/pg/jobs/:jobid, GET /admin/cron/pg/jobs/:jobid/runs |
| Application jobs | GET /admin/cron/app/jobs |
The pg_cron listing returns extensionMissing, and the screen offers to install the extension (POST /admin/schema/extensions/pg_cron) instead of failing. CronRunDrawer shows run history with JobStatusIcon. See cron jobs.
Realtime
realtime.api.ts reads and writes the realtime config (GET/PATCH /admin/realtime/config, plus the respect-rls toggle) and lists channels, events and presence. RealtimeConfigPanel is the editor. See Realtime.
RLS access matrix
The Studio screen is Authentication → Role Access (/p/:id/auth/access). It
shows, per table, what anon and authenticated can read, insert, update and
delete according to the real PostgreSQL policies and grants. It is a read-only
view; it does not execute arbitrary SQL and does not change policies.
The query simulator still exists as an operator/automation capability, but is not a Studio page. The CLI and MCP expose it through the admin API:
POST /admin/rls/simulate { sql, ...caller, limit }
POST /admin/rls/simulate/diff { sql, left, right }It runs a query as a chosen caller and always rolls the transaction back. See the RLS guide and MCP Server for the CLI/MCP workflow.