Skip to content

Architecture

Strawly is built as a set of independently deployable Docker services orchestrated by Docker Compose. This page describes how the pieces fit together.

Services

Backend

  • Language: TypeScript / Node.js
  • Framework: Express.js
  • Port: 3001 (default)
  • Purpose: Central REST API. Handles authentication (JWT), user management, and aggregates data from all modules for the frontend.
  • Database: PostgreSQL via Prisma ORM
  • Schema ownership: The backend is the sole owner of the database schema. It runs all migrations. Modules never run migrations.

Frontend

  • Language: TypeScript / Node.js
  • Framework: Next.js
  • Port: 3000 (default)
  • Purpose: Web UI. Communicates exclusively with the backend API — it does not talk to modules or the database directly.

PostgreSQL

  • Port: 5432 (internal only — not exposed to the host by default)
  • Purpose: Shared relational database for all services
  • Data separation: Each module writes to its own prefixed tables (az_*, aws_*, gcp_*)

Migrations (one-shot container)

  • Runs prisma migrate deploy against the database on startup
  • Exits after completion — it is not a long-running service
  • Starts before the backend to ensure schema is up to date

Seeder (one-shot container)

  • Seeds initial data (system roles and the default admin account from ADMIN_EMAIL / ADMIN_PASSWORD)
  • Runs after migrations and exits
  • Safe to run on an already-seeded database (idempotent)

The module registry is seeded separately by the seed-modules script, not by this container. See Module registry below.

Modules (optional)

Each module is an independent service. See Modules Overview for the current list.

Data flow

User → Frontend (Next.js :3000)
         ↓ REST API calls
       Backend (Express :3001)
         ↓ Prisma ORM
       PostgreSQL (:5432)
         ↑ Prisma ORM (read/write to prefixed tables)
       Module (e.g. :3002)
         ↑ Azure / AWS APIs (external)

The frontend never communicates with modules directly. The backend queries the shared database where modules have written their findings.

Startup order

The generated compose starts services in dependency order:

  1. postgres — database
  2. migrations — runs prisma migrate deploy, waits for postgres to be healthy, then exits
  3. seeder — runs prisma/seed.ts, waits for migrations to complete, then exits
  4. backend — API, waits for the seeder to complete successfully
  5. modules — optional services, wait for the backend to be healthy, start in parallel
  6. frontend — UI, waits for the backend to be healthy

Health checks and service_completed_successfully conditions gate each tier from starting.

Module registry

The module registry lives in the backend's settings table as global rows keyed module:<provider>:<...>. It is populated by the seed-modules script (npm run seed-modules), which reads strawly-deployment.yml and upserts one settings entry per module with its display name, icon, service URL, schedule, and enabled flag. The frontend reads this registry to know which module sections to show. Disabling a module in the YAML and re-running the seeder flips its entry to disabled and the UI hides its section.

Repository structure

Each service lives in its own git repository and is built into its own Docker image:

Repository Image Description
Strawly/backend git.strawly.app/Strawly/strawly-backend Express.js API
Strawly/frontend git.strawly.app/Strawly/strawly-frontend Next.js UI
Strawly/optimizations-azure git.strawly.app/Strawly/optimizations-azure Azure module
Strawly/deployment Orchestration scripts and config

Override the full image reference per service with BACKEND_IMAGE, FRONTEND_IMAGE, or a module's image field in strawly-deployment.yml. Images are built by CI on every push to main and on version tags.

Secrets and configuration

All sensitive configuration is passed via environment variables at runtime (never baked into images). The deployment repository provides:

  • .env.example — template with all required variables
  • generate-secrets.sh — prints cryptographically random values for JWT_SECRET, CREDENTIALS_ENCRYPTION_KEY, and POSTGRES_PASSWORD to paste into .env