Skip to content

Architecture overview

Tradr is deliberately small: a static React SPA, a stateless Hono API, and one PostgreSQL database. There is no Redis, queue, or background worker in the default self-hosted stack — the three containers in Self-host with Docker Compose are the whole system.

  • web — Nginx serving the built static SPA and reverse-proxying /api to the api service. It is the only container that publishes a host port (${WEB_PORT:-8080} → container :80).
  • api — a Node.js Hono server. It is stateless: all persistent state lives in Postgres, so the api can be restarted or scaled without data loss.
  • postgres — PostgreSQL 16 with a persistent named volume (pgdata).

api and postgres are not published — they are reachable only inside the compose bridge network, keeping the database and API off the public internet by default.

On boot the api runs its migrations automatically inside bootstrap() before it serves traffic, serialised by a PostgreSQL session-level advisory lock (apps/api/src/db/migrate.ts). Migrations are forward-only — there is no down/rollback — which is why the upgrade policy is back up before every upgrade.

Every endpoint is mounted under /api and documented in the generated API reference (in the sidebar under Self-hosting & Development → Reference), which is produced from the API source so it always matches what the app serves.