Skip to main content

What It Is

packages/api/src/app.ts’s createApp() is the single function that assembles every HTTP route this system exposes into one Express application, in a specific, deliberate mounting order. Everything an external caller can reach, from an unauthenticated health check to a signed execution request, passes through this one file’s wiring.

Why It Was Built

An HTTP API needs a consistent, auditable answer to two questions for every route: does this route require a caller identity, and what happens when something goes wrong. app.ts answers both in one place rather than leaving each route handler to decide independently, which is what makes it possible to state precisely (not just claim) which routes are public and why.

How It Works

Mounting order, and why it matters

Routes are mounted in this order, and the order is load-bearing: caller-auth middleware (createCallerAuthMiddleware) is only added to the pipeline partway through, so everything mounted before it is unauthenticated by construction, not by an exception carved out of a uniform rule. (packages/api/src/app.ts:154-347)

Error mapping

createErrorHandler() (packages/api/src/middleware/error-handler.ts) is the single place a thrown domain error becomes an HTTP response: (packages/api/src/middleware/error-handler.ts:95-201)

The full route inventory

Every route module lives in packages/api/src/routes/:

trust proxy

app.set("trust proxy", 1) (packages/api/src/app.ts:147) trusts exactly one proxy hop , this codebase’s actual deployment fronting (Fly.io’s edge, per fly.toml’s force_https). Without this, req.ip and req.protocol/req.secure reflect the proxy’s own connection to the process, not the original client’s.

How It Enables Things, With a Concrete Example

examples/tutorials/102-distinguishable-http-status demonstrates the NonceAlreadyConsumedError vs. generic-RuntimeError distinction in the error handler directly, a policy denial and a replay attempt against the same endpoint return genuinely different, distinguishable status codes rather than both collapsing into an opaque failure.

How to Validate This Yourself

  • packages/api/src/app.ts, the actual mounting order; read it top to bottom rather than trusting the table above once this file changes.
  • packages/api/src/middleware/error-handler.ts, the full error-to-status mapping.
  • packages/api/src/routes/*.ts, one file per route group.
  • packages/api/tests/integration/*.integration.test.ts, most route groups have a corresponding integration test that exercises the real HTTP path, not just the handler function in isolation.

Integration Requirements

createApp() requires a CallerAuthOption, either a real { authenticator, auditSink } pair or the literal string "disabled" (used only for local development and the tutorial suite; there is deliberately no default, so omitting this choice is not possible). Optional: RateLimitOption (Chapter 16), stepUpVerifier and policyChangeApprovalService (both required in effect, though optional at the type level, once caller-auth is enabled and the Policy Governance approve/reject endpoints are actually reachable, see Chapter 7).