API template: overview
The API layer owns security, data, and background work: auth, sessions, OAuth, email, queues, audit log, Stripe billing, structured logging, and an env validator that refuses to boot if anything is missing.
apps/api/ is built on Bun (runtime), Elysia (HTTP framework), Drizzle (ORM), Postgres (durable state), Valkey (cache/queues), and BullMQ (background jobs). A feature folder splits into routes (HTTP + TypeBox validation), service (logic + database queries), and types (shared interfaces). Lint rules forbid cross-imports that would blur the split.
Design
Section titled “Design”Per-feature folders contain one concern each. Adding product behavior means one folder, not scattered route/service/model edits. Routes, services, and types stay separate by lint rule: a *.routes.ts that imports drizzle-orm fails the build, and a *.service.ts that imports Elysia’s t does too. The split survives refactors and agent-written code.
Config is frozen at boot. process.env is read in one validator; misconfigured deployments fail before serving traffic. Infrastructure is pluggable: email, AI, cache, and queues swap through config. Dev runs without vendor keys.
Drizzle provides TS-first models with SQL-shaped schema and real migration files. OpenAPI emits the UI boundary: the React app calls a generated client, so server changes become type errors instead of runtime surprises.
File layout
Section titled “File layout”src/ contains:
index.ts: Entrypoint (env, Sentry, queues, listen)config/: App composition, env, logger, queue bootstrapapi/: Feature folders (auth, users, accounts, dashboard, billing, admin, health, notifications)clients/postgres/: Drizzle client and per-domain schema moduleslib/: Shared utilities (auth, email, audit-log, AI, cache, errors, notifications)middleware/: Per-route Elysia pluginsqueues/: BullMQ queue and worker pairstemplates/email/: Handlebars sources, compiled to JSON at build
A feature folder (e.g., src/api/posts/) always has:
posts.routes.ts: HTTP surfaceposts.service.ts: Business logic and database queriesposts.types.ts: Shapes shared between routes and serviceposts.schemas.ts(optional): TypeBox request/response validation
The shipped modules (auth, users, accounts, billing, dashboard, admin, health, notifications) are framework infrastructure. api/ is yours to fill with domain resources. The account-scoped resource pattern is enforced by the drizzle-conventions/account-scoped-tables-require-where lint rule, so every new account-scoped resource inherits isolation for free. Add new resources with bun run new:resource <name>; the scaffolder writes the anatomy and wires it into config/routes.ts. The First feature in 10 minutes tutorial walks the full backend and UI loop.
Cross-cutting concerns
Section titled “Cross-cutting concerns”- Auth: Cookie sessions, short-lived access JWT cookies, DB-backed refresh sessions. See Authentication.
- Tenant: Accounts are the tenant boundary; users join via memberships with roles. See Multi-tenant model.
- ACL: Server-authoritative CASL ability, plan and feature gates. See ACL & feature resolution.
- Billing: Stripe Checkout, Customer Portal, raw-body webhooks, DB-backed idempotency. See Billing.
- Email: Pluggable provider (Cloudflare, Resend, SendGrid, SMTP), precompiled templates, queue-aware dispatch. See Email.
- Queues: BullMQ with QueueManager and inline fallback. See Queues.
- Audit: Fire-and-forget append-only event log. See Audit log.
- Env: TypeBox shape plus hand-written invariants at boot. See Env validator.
Lint as the contract
Section titled “Lint as the contract”The architecture is held in place by a family of custom ESLint plugins. bun run validate is the merge gate. See Lint as the contract for why these rules matter.
Related
Section titled “Related”- Lint as the contract; the family of plugins that keep these layers apart.
- lint:meta rules; static repo enforcement rules under
scripts/lint-meta/. - Scripts & tooling; command → script map for apps/api.
- Authentication; cookie sessions and refresh on the same spine.
- ACL & feature resolution; server-authoritative permissions and plan gates.
- Multi-tenant model; accountId as the scoping mechanism.
- Env validator; the boot guard refusing misconfigured deploys.