Surplus logo
Surplus Docsby Sharing Excess
The Stack

API server

apps/server is the heart of Surplus. It handles every read and write, owns all business logic, and is the only component that touches the database, cache, and file storage. It's a Hono app running on Bun, with ORPC providing the typed API surface.

Entry point

The server starts from apps/server/src/index.ts, preloaded with apps/server/src/instrumentation.ts so OpenTelemetry is initialized before anything else:

# production
bun --preload ./src/instrumentation.ts ./src/index.ts

# local dev (hot reload)
bun run --hot --preload ./src/instrumentation.ts src/index.ts

It listens on port 8080 (or PORT).

What's in index.ts

index.ts assembles the Hono app and its middleware pipeline — Apitally logging, security headers, request context, rate limiting, CORS, and the ORPC handler — then starts the server with Bun.serve(). The exact order and reasoning are covered in Request lifecycle.

It also exposes two non-ORPC endpoints generated from the same contract:

  • /openapi.json — the OpenAPI 3 spec, generated from the ORPC router.
  • /openapi — an interactive Scalar API reference UI.

Layout

apps/server/src/
  index.ts                 — app assembly + server start
  instrumentation.ts       — OpenTelemetry preload
  routes/                  — one file per endpoint, mirroring the contract
    hubs/list.route.ts
    auth/otp/request.route.ts
    files/serve.route.ts
    ...
  helpers/
    orpc.ts                — assembles all route handlers into the router
    middleware.ts          — authMiddleware, error mapping
    services.ts            — dependency injection: builds service instances
    env.ts                 — Zod-validated runtime configuration
    rateLimit.ts           — Redis-backed rate limiters

The routes/ tree mirrors the contract: each contract entry has a matching *.route.ts handler, and helpers/orpc.ts wires them all into a single router.

Dependencies it injects

helpers/services.ts is where the real world gets wired in. It constructs the database clients (primary and read replica), the Redis client, the S3 client, and the Stripe and Resend integrations, then injects them into the service classes from @surplus/services. Services themselves never read environment variables — they receive everything they need.

Configuration

All runtime configuration is validated up front in apps/server/src/helpers/env.ts. The server Dockerfile runs an env-check at build time so a misconfigured deploy fails fast rather than at runtime. Required values include the database connection strings, Redis credentials, S3 keys, the JWT signing secret, and Stripe/Resend keys.

Deployment

The server builds via apps/server/Dockerfile and runs on Railway (three replicas in production). See Build and deploy and, for the security posture of these dependencies, Infrastructure and secrets.