Skip to content

D1

Purpose

The one relational database backing the API Worker and 3 apps' own portal/admin logic.

Database / binding names

  • Database name: akshaya-group-global
  • Binding: DB (identical in workers/api, apps/corporate-web, apps/software-web, apps/admin-web - see Architecture → Data Platform)
  • database_id: committed in each wrangler.jsonc (shown here as <D1_DATABASE_ID> for consistency with this portal's placeholder convention, even though it is a resource identifier rather than a credential and is safe to have in git - see docs/DEPLOYMENT.md §2). Look up the real value with wrangler d1 list or by reading workers/api/wrangler.jsonc directly.

Schema / migrations

9 migration files in workers/api/migrations/, applied in filename order, all additive-only (new tables/columns, never DROP/rename/type-change) - see Architecture → Data Platform for the full list and what each adds.

Local vs. remote commands

Local (emulator, safe) Remote (production D1)
Apply migrations wrangler d1 migrations apply akshaya-group-global --local (from workers/api), or npm run db:local -w @akshaya/api-worker [REMOTE / MUTATING] npm run migrate:api:remote, or wrangler d1 migrations apply akshaya-group-global --remote --config workers/api/wrangler.jsonc
List applied migrations wrangler d1 migrations list akshaya-group-global --local [READ-ONLY, REMOTE] wrangler d1 migrations list akshaya-group-global --remote --config workers/api/wrangler.jsonc
Query wrangler d1 execute akshaya-group-global --local --command "SELECT ..." [READ-ONLY if SELECT, REMOTE] wrangler d1 execute akshaya-group-global --remote --config workers/api/wrangler.jsonc --command "SELECT ..."

docs/DEPLOYMENT.md §4 is explicit: "This repo's automation must never run wrangler d1 migrations apply --remote outside of the deploy-production CI job." Treat remote migration application as part of the deploy, never a standalone action from a laptop.

Safe SELECT / read commands (production)

# [READ-ONLY, REMOTE] - requires CLOUDFLARE_API_TOKEN/ACCOUNT_ID or `wrangler login`
wrangler d1 execute akshaya-group-global --remote --config workers/api/wrangler.jsonc \
  --command "SELECT COUNT(*) FROM contacts;"

wrangler d1 migrations list akshaya-group-global --remote --config workers/api/wrangler.jsonc
A plain SELECT through wrangler d1 execute --remote reads production data but changes nothing - still requires real Cloudflare credentials, so it is not something to run casually from an unattended process; treat it the same as any other authenticated production access.

Backup / export

No automated D1 backup exists in this repository - no scheduled export, no scripts/*backup* for D1 (checked: scripts/ contains only cloudflare-bootstrap.sh, the smoke-test scripts, and sync-portal-shared-css.mjs). The available, unautomated option is Wrangler's own export:

# [READ-ONLY, REMOTE] - produces a local .sql file, does not modify the database
wrangler d1 export akshaya-group-global --remote --config workers/api/wrangler.jsonc --output backup-$(date +%Y%m%d).sql
Documented here as a capability, not as an existing automated safety net - see Operations → D1 Operations for when/how to actually use it.

Troubleshooting

Symptom Likely cause
database not found database_id in a wrangler.jsonc doesn't match the account being deployed to - compare against wrangler d1 list
Migration step reports a table/column already exists Already partially/fully applied (e.g. a manual run outside CI) - check wrangler d1 migrations list ... --remote before hand-editing anything
A query works --local but not --remote Local emulator schema has diverged from production - re-run db:local from a clean state, or confirm the remote migrations are actually up to date

Recovery

There is no "undo" for an applied migration - see docs/DEPLOYMENT.md §5 "D1 migrations must stay backward-compatible" and Operations → Rollback. Recovery from a genuinely bad write (not a bad migration) would mean restoring from the manual export above, if one exists - there is no other recovery path today.

Security considerations

  • Every remote D1 command requires CLOUDFLARE_API_TOKEN/CLOUDFLARE_ACCOUNT_ID - never embed these in a script or commit them.
  • database_id is not secret; CLOUDFLARE_API_TOKEN is - do not confuse the two when deciding what's safe to paste into a PR/issue.