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: Modules scope their data using a provider field on shared tables (e.g. provider: 'azure')

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 (e.g. the default admin account, module registry entries)
  • Runs after migrations and exits
  • Safe to run on an already-seeded database (idempotent)

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 scoped by provider)
       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

Services start in dependency order:

  1. postgres — database
  2. migrations — schema migration (waits for postgres)
  3. seeder — initial data (waits for migrations)
  4. backend — API (waits for seeder)
  5. modules — optional services (wait for backend and postgres, start in parallel)
  6. frontend — UI (waits for backend)

Health checks on each service gate the next tier from starting.

Module registry

The backend maintains a modules table (seeded by the seeder and updated by modules at startup). The frontend reads this registry to know which module sections to display in the sidebar. When a module is disabled and removed, its registry entry becomes inactive 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 codeberg.org/strawly/strawly-backend Express.js API
Strawly/frontend codeberg.org/strawly/strawly-frontend Next.js UI
Strawly/optimizations-azure codeberg.org/strawly/strawly-optimizations-azure Azure module
Strawly/deployment Orchestration scripts and config

Images are built by Forgejo Actions 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 — generates cryptographically random values for JWT_SECRET and CREDENTIALS_ENCRYPTION_KEY

Licensing

The backend, frontend, and all modules are dual-licensed under AGPL-3.0-or-later (open source) and a commercial license. For commercial licensing enquiries, get in touch.