Skip to main content

The database

The database is AWS RDS Postgres 17 on a db.t4g.micro instance. It has 20 GB of storage, and the storage grows automatically. Storage is encrypted, and deletion protection is on. Backups run every day. RDS keeps them for 14 days, with point-in-time recovery inside that window. A restore ran on September 18, 2026. See Monitoring and recovery. The database is in a private subnet. Only the API’s tasks can reach it. To run a query in production, see Run a command inside AWS.

Tables

The schema is in packages/db/src/schema.ts and uses Drizzle. The schema follows these rules:
  • One requests table for every form. The type column and the subject columns carry the differences.
  • One taxonomy_terms table for every facet, such as disease states and report types.
  • Event tables are append-only. Nobody edits or deletes a row in request_events or billing_events. The admin shows these rows as the history.
  • Request history is permanent. Database triggers refuse a delete on requests and quotes, and an edit or delete on request_events. Test cleanup lifts this for one transaction with the oncoeverything.allow_history_delete setting.
  • No generic item table. Each catalog has its own typed table.
docs/architecture/content-model.md explains these rules in full. Read it before you add a table or a route.

Migrations

1

Change the schema

Edit packages/db/src/schema.ts.
2

Generate the migration

Commit the new file in packages/db/migrations.
3

Apply it locally

4

Push

In production the API applies pending migrations when a new task starts, before its port opens. The health check cannot pass until they are done.
A rollback of the API does not undo a migration. Fix a bad migration with a new one that moves forward.

Directory data

The recruiter, education, podcast and other directory listings are not in the database. packages/directories builds them as JSON files from the client’s spreadsheets. The web app reads the files at build time.