Supabase
Run SQL against a Supabase project through the management API: querying data, schema changes (DDL), policies (RLS), storage metadata, and migrations. SQL runs with management API privileges, so treat it as admin-level database access.
Everything goes through one script, scripts/supabase.sh, with three commands:
env— manage connection environments (stored inenvironments.json)sql— run a SQL stringsql-file— run SQL from a file
Environments
Each environment is a named entry in environments.json (gitignored, chmod 600)
holding a url and an optional access_token. Manage them with the env
subcommands:
# List configured envs (tokens masked)
scripts/supabase.sh env list
# Add an env (--url required; --access-token and --description optional)
scripts/supabase.sh env add dev --url https://<dev-ref>.supabase.co --access-token sbp_...
scripts/supabase.sh env add prod --url https://<prod-ref>.supabase.co --description "Production"
# Update one or more fields on an existing env
scripts/supabase.sh env update dev --url https://<new-ref>.supabase.co --access-token sbp_...
# Inspect one env: human (default), json, or shell export
scripts/supabase.sh env get dev
scripts/supabase.sh env get dev --format json
scripts/supabase.sh env get dev --format export
# Remove an env (refused if it is the only one)
scripts/supabase.sh env remove prod
# Verify at least one env is configured (non-zero exit otherwise)
scripts/supabase.sh env check
Rules the script enforces:
env addrequires--url; the env name must be unique.env removeis refused on the last remaining env.- The access token is a secret. If an env has none stored, the shell
SUPABASE_ACCESS_TOKENis used instead. environments.jsonis read from the skill root, or~/.config/claude/supabase-environments.jsonif that is where it already exists.
If the script reports no environments configured or SUPABASE_URL not set, the
user has not finished setup: run env add to create the first env, or point them
at the README.
Selecting an environment
Pick the environment per command with --env <name>. Resolution order:
1) --env <name> set -> use that env from environments.json
2) SUPABASE_URL + TOKEN exported -> use those exported values
3) exactly one env configured -> use it (--env optional)
4) multiple envs, none named -> error: pass --env <name>
When the user names an environment ("in prod", "on dev"), pass --env <name>. If
multiple envs exist and the user did not name one, ask which to use before running.
Running SQL
# Run a SQL string
scripts/supabase.sh sql --env dev "SELECT * FROM users LIMIT 5"
scripts/supabase.sh sql --env prod "SELECT count(*) FROM users"
scripts/supabase.sh sql --env dev "INSERT INTO users (name, email) VALUES ('Alice', 'alice@test.com')"
# Run SQL from a file (prefer for long or multi-statement SQL)
scripts/supabase.sh sql-file --env dev ./migrations/001_init.sql
- Quote the SQL. Escape inner double quotes (e.g. policy names:
\"items_read_own\"). sqlsends the whole string as one query, so multiple;-separated statements run together.- SQL that contains
$— dollar-quoted function/trigger bodies ($$ … $$) or positional params ($1) — must go throughsql-file. In a double-quotedsqlargument the shell expands$$(to its PID) and$1, corrupting the statement. File contents are sent literally, sosql-fileis safe.
Reading the output
Output is the raw management API JSON, piped through jq:
- A successful query returns its result as JSON (an array of row objects for
SELECT). - A rejected query returns an object with a
messagefield, e.g.{"message": "syntax error at or near ..."}. Detect failure by checking for a lonemessagekey — the HTTP call (and exit code) usually still succeeds even when the SQL is rejected.
Safety
These commands run with management API (admin) privileges, so a bad statement can drop data or break a live project. Before running:
- Confirm destructive statements with the user —
DROP,TRUNCATE, and anyDELETE/UPDATEwithout aWHEREclause — especially against a production env. - Confirm the target env for writes. If multiple envs are configured and the
request is a write or DDL, verify which env (
--env <name>) before running; do not let a write fall back to an auto-selected env. - Explore read-only first. Prefer a
SELECT ... LIMITto inspect data and schema before mutating it. - Make migrations atomic. Wrap multi-step changes in
BEGIN; ... COMMIT;(viasql-file) so a mid-way failure rolls back. NoteCREATE INDEX CONCURRENTLYcannot run inside a transaction — run it as a standalone statement. - Make migrations re-runnable. Prefer
CREATE TABLE IF NOT EXISTS,CREATE OR REPLACE,ADD COLUMN IF NOT EXISTS, andDROP ... IF EXISTS.
SQL reference
For ready-made examples covering DDL, indexes, enum types, views, functions/RPC, triggers, RLS, data patterns (upsert, JSONB), pgvector similarity search, storage, and schema introspection, see references/sql-cookbook.md.