Supabase Development Guide
Environment Files
| File | Target | Description |
|---|---|---|
functions/.env.local | Local Supabase | localhost:54321/54322, deterministic demo keys |
functions/.env.dev | Remote dev | Project jfmdrdleyyclgqjjowxt (eu-west-1) |
functions/.env.prod | Remote prod | Project aqbchetcfyqtyrdjznvz (eu-central-1) |
functions/.env.example | Template | All required vars with placeholders |
No bare
.envfile. All commands require explicit--env-fileto prevent accidental env mix-ups.
Local Development
1. Start Supabase local stack
pnpm supabase:start
# or from supabase/: pnpm startThis starts Postgres, Auth, Storage, Realtime, Studio and applies all migrations.
2. Serve the API function
pnpm --filter @scalex/supabase fn:serve:local3. Seed the database
Requires the function to be running (step 2).
pnpm --filter @scalex/supabase seed:localThis calls POST /api/admin/seed-database with clean strategy (wipes and re-inserts all seed data).
4. Access Swagger UI
- Swagger UI: http://localhost:54321/functions/v1/api/ui
- OpenAPI spec: http://localhost:54321/functions/v1/api/doc
- Supabase Studio: http://localhost:54323
Remote Dev Environment Setup
First-time setup
# 1. Push schema migrations to the dev project
pnpm --filter @scalex/supabase deploy:dev
# 2. Seed the remote dev database via local function serve
# Terminal 1 — serve functions locally, pointing at remote dev DB:
pnpm --filter @scalex/supabase fn:serve:dev
# Terminal 2 — seed through the local serve (writes to remote dev DB):
pnpm --filter @scalex/supabase seed:dev:localWhy
seed:dev:localinstead ofseed:dev?seed:dev:localcalls the local function URL (127.0.0.1:54321) whilefn:serve:devis running with.env.dev. This means the function writes to the remote dev DB without needing to deploy the function first. Useseed:devonly after the function is deployed to the remote project.
After deploying the function to remote dev
# Deploy function and set secrets
supabase link --project-ref jfmdrdleyyclgqjjowxt
supabase functions deploy api --no-verify-jwt
supabase secrets set --env-file functions/.env.dev
# Seed directly via remote function
pnpm --filter @scalex/supabase seed:devDay-to-day dev against remote
# Serve functions locally, pointing at remote dev DB
pnpm --filter @scalex/supabase fn:serve:devSchema Migrations (Drizzle code-first)
Schema is defined in packages/db/schema/ using Drizzle ORM.
# 1. Edit schema in packages/db/schema/
# 2. Generate migration SQL
pnpm db:generate
# 3. Apply locally
supabase db reset
# 4. Apply to remote dev
pnpm --filter @scalex/supabase migrate:devSeeding
Seeding is separated from schema migrations by design:
- Schema: applied via
supabase db push(migration files) - Seed data: applied via
POST /api/admin/seed-database(API endpoint)
This allows seeding any environment (local, dev, prod) without destructive db reset.
Strategies
| Strategy | Behavior | Use case |
|---|---|---|
clean | Deletes all data, then inserts | Fresh start, local dev |
upsert | Updates existing by ID, inserts new | Incremental, safe for shared envs |
Seed data
Defined in functions/api/lib/seeding/data/:
- 25 Ukrainian oblasts, 10 locations, 10 scales with cameras
- 2 test customers (Clerk orgs), 2 contacts
- 3 sample orders with payments, receipts, photos
- Scale commands, sessions, heartbeats, weight logs
- App remote configs
Seeding respects FK dependencies (21 tables in order).
"secrets:dev": "supabase link --project-ref jfmdrdleyyclgqjjowxt && supabase secrets set --env-file functions/.env.dev",
Available Scripts (supabase/package.json)
| Script | Description |
|---|---|
start | Start local Supabase (if not running) |
reset | Reset local DB (migrations + seed.sql) |
fn:serve:local | Serve functions against local Supabase |
fn:serve:dev | Serve functions against remote dev |
fn:serve:prod | Serve functions against remote prod |
seed:local | Seed local DB via local function (requires fn:serve:local) |
seed:dev:local | Seed remote dev DB via local function (requires fn:serve:dev) |
seed:dev | Seed remote dev DB via deployed remote function |
deploy:dev | Deploy functions to dev project |
migrate:dev | Link to dev project and push migrations |
secrets:dev | Set secrets to dev project |
test | Run Supabase DB tests |
lint | Lint SQL |
Root scripts (turbo delegations)
| Script | Description |
|---|---|
supabase:start | turbo start --filter @scalex/supabase |
supabase:types | Generate TS types from remote schema |
supabase:db:reset | Reset local database |
db:generate | Generate Drizzle migration SQL |
Notes
--no-verify-jwtskips JWT auth check for local/dev testing- Swagger UI auto-injects
x-admin-keyheader fromADMIN_API_KEYenv var - Local Supabase keys are deterministic demo values (safe to commit)
- Clerk third-party auth is configured in
config.toml - Storage bucket
weight-photosis auto-created locally viaconfig.toml
Why DATABASE_URL uses db.supabase.internal:5432 locally
The Edge Runtime runs inside Docker (supabase_edge_runtime_hono container). From within the container, localhost refers to the container itself — not the host machine. All Supabase local services share the supabase_network_hono Docker network, so the Postgres container is reachable via its Docker alias db.supabase.internal on its internal port 5432 (not the host-exposed 54322).