Skip to content

Working on the API

This page is for contributors working on Lombok’s backend — the NestJS API in packages/api that acts as Lombok’s control plane.

It covers where API code lives, how the API fits into Lombok’s runtime boundaries, and how to run the relevant checks locally. It does not repeat environment setup — for that, start with Local Development with dx.

Two different kinds of “backend work.” Contributing to the core API / control plane (this page) is different from writing the backend logic of an app that runs on top of the platform. If you are building an app’s backend, skip to Core API work vs. app-level backend work.

Lombok is a Bun workspace monorepo, so backend work is not in a single src/ folder. The main anchors are:

  • packages/api — the main NestJS backend and control plane
  • packages/types — shared contracts and types used across runtime boundaries
  • packages/sdk — the typed client SDK built around the platform API contract

Per the Repository Conventions routing guide, the rule is direct: backend logic, auth, storage, tasks, sockets, and platform services → packages/api.

Following the Package Map, packages/api is the control plane and owns concerns including:

  • auth and users
  • folders, storage, and object metadata
  • events and tasks
  • app platform services and app configuration
  • Docker integration
  • comments, search, and notifications
  • sockets (realtime gateways)
  • MCP
  • ORM integration

The Architecture Overview frames this package as the control plane: the layer responsible for identity, platform logic, task orchestration, and realtime updates.

The API sits at the center of Lombok’s runtime boundaries. As described in Runtime Boundaries, the boundaries that matter most when working in packages/api are:

  • API ↔ browser. The main UI is a client of the backend, not the system of record. It reaches the API through authenticated HTTP/API calls and realtime Socket.io communication.
  • API ↔ PostgreSQL. The backend stores platform/application state — users, folders, task records, app/platform configuration, metadata — in PostgreSQL through Drizzle ORM.
  • API ↔ S3-compatible object storage. Platform state in PostgreSQL is kept distinct from file/object data in object storage.
  • API ↔ worker runtime. The API decides what work should run; the worker runtime executes it. This is why app/runtime work is not treated as ordinary inline API logic.
  • Task dispatch transport. Lombok’s documented mental model uses a DB-backed task table and Socket.io room-based dispatch between the API and the worker-side execution model — there is no assumed external Redis/BullMQ/RabbitMQ queue.
  • Auth boundary. The architecture highlights JWT-backed auth flows across major communication paths.

Keep these boundaries in mind: changes in packages/api sit upstream of the worker runtime’s execution and are the system of record the UI reads from.

API development uses the same Docker-based local loop as the rest of the repository, driven by the ./dx developer CLI. The full setup — prerequisites, ./dx up, PostgreSQL and MinIO, and the /etc/hosts entry — is documented in Local Development with dx; this page does not duplicate it.

To reload or restart just the API service:

Terminal window
./dx reload api
./dx restart api

To follow logs from the running environment:

Terminal window
./dx logs

For the full list of ./dx commands, run:

Terminal window
./dx help

The API stores platform state in PostgreSQL through Drizzle (not Prisma). The documented database workflow is driven through ./dx, for example:

Terminal window
./dx db migrate

For the broader database workflow and seeding, see Local Development with dx. A dedicated migrations page is planned for deeper workflow guidance.

Lombok uses a shared contract model and OpenAPI-related tooling, with the typed client surface in packages/sdk built around the platform API contract. The documented generation commands are:

Terminal window
./dx generate openapi
./dx generate metadata

When you change the API surface, keep the shared contract and SDK in mind so consumers stay aligned. See the Core SDK page for how the typed client relates to the platform API.

Use ./dx for the standard contributor checks. Linting, formatting, and type-checking are shared across the monorepo:

Terminal window
./dx check all
./dx check eslint
./dx check prettier
./dx check tsc

Run unit tests for the API package, and the API end-to-end suite, with:

Terminal window
./dx unit api
./dx e2e api

Refer to Local Development with dx for how these commands fit into the wider workflow. Dedicated pages for tests and linting/type-checking are planned for this docs wave.

These are two different surfaces, and it is worth being explicit about which one you are on:

  • Working on the core API (this page) means changing Lombok’s control plane — auth, storage, tasks, sockets, app-platform services, and the rest of the responsibilities owned by packages/api.
  • Building an app’s backend means writing the logic an app declares and ships, which runs on top of the platform as runtime workers rather than as inline API code.

If you are building app-level backend logic, the relevant docs are:

The connecting idea: the API decides what work should run, and app backend logic is what runs through the worker runtime. Control-plane contributors work on the former; app authors work on the latter.

When deciding where a backend change belongs:

  • platform logic, auth, storage, tasks, sockets, or app-platform services → packages/api
  • shared contracts and schemas crossing runtime boundaries → packages/types
  • the typed client surface for API consumers → packages/sdk
  • executing work once the API has dispatched it → the worker runtime in packages/core-worker (see Working on Workers)
  • the backend logic an app declares and ships → the app-platform surface and App Worker SDK, not packages/api

Lombok is organized by runtime responsibility rather than by a single app folder, so matching your change to the right package keeps the boundaries clean.