Architecture Overview

Full-stack TypeScript monorepo built on Bun. Backend serves the frontend as a SPA in production; in development both are served by the same Bun.serve() process with HMR.

Stack

LayerTechnologyNotes
Runtime & bundlerBunReplaces Node + npm + webpack/Vite + Jest
HTTP serverBun.serve()No Express/Fastify; routes declared as plain object
Frontend frameworkReact 19StrictMode; functional components only
UI libraryMUI v7 (Material UI)Emotion styling engine
Client stateRedux Toolkit + RTK QueryRTK Query for server state; slices for client state
Routing (frontend)React Router v7SPA client-side routing
ORMDrizzle ORMSQL-first; $inferSelect / $inferInsert types
DatabasePostgreSQLAccessed via postgres driver
Authjose JWT + HttpOnly cookiesHS256 access tokens; hashed refresh tokens in DB
ValidationZod v4Shared schemas for request bodies
Testingbun:test + happy-domFrontend: happy-dom DOM; backend: pure unit tests
E2E testingPlaywrightAPI tests + browser tests; Docker-isolated CI suite
Lintingoxlint + oxfmt50–100× faster than ESLint + Prettier
Dead codeknipDetects unused exports and dependencies

Request Flow

Browser / Client
       │
       ▼
 Bun.serve()  ─────────────────────────────────────────────────
       │                                                        │
       ▼                                                        ▼
  /api/* routes                                         /* (SPA fallback)
       │                                                public/index.html (dev)
       ▼                                                dist/  (production)
 withMiddleware()
  ├─ authMiddleware      (verifies Bearer JWT)
  ├─ signupTokenMiddleware (verifies signup JWT for /set-password)
  ├─ authRateLimit       (10 req/min per IP on auth endpoints)
  └─ requireRole(...)    (RBAC — admin | user)
       │
       ▼
  Controller
  (parse req, validate body/params with Zod, call service)
       │
       ▼
  Service  ──→  ErrorOr<T>
  (business logic, strip sensitive fields, sign tokens)
       │
       ▼
  Repository
  (Drizzle queries: SELECT / INSERT / UPDATE / DELETE)
       │
       ▼
  PostgreSQL

Directory Structure

bun-boiler/
├── backend/
│   ├── controllers/      HTTP layer — parse, validate, call service, return Response
│   ├── db/
│   │   ├── client.ts     Singleton Drizzle connection; pingDb() with 5-attempt backoff
│   │   ├── migrations/   Generated SQL files — never edit manually
│   │   ├── schemas/      Source of truth for all DB types
│   │   └── seed.ts       Idempotent admin user seed
│   ├── features/
│   │   ├── mail/         Optional Nodemailer SMTP (no-op when SMTP_HOST unset)
│   │   ├── telemetry/    Optional OTel traces + metrics + logs (no-op when OTEL_ENDPOINT unset)
│   │   └── validation/   Zod schemas + validateRequest / validateParam helpers
│   ├── middleware/       authMiddleware, signupTokenMiddleware, rateLimitMiddleware, requireRole
│   ├── repositories/     Drizzle queries — data access only, no business logic
│   ├── routes/           URL → handler mapping; spread into Bun.serve() routes
│   ├── services/         Business logic; returns ErrorOr<T>
│   ├── types/            Shared backend types (ErrorOr, AppError, AppJwtPayload, …)
│   ├── utils/
│   │   ├── auth/         JWT signing/verification, refresh token helpers, cookie builders
│   │   ├── response/     successResponse, serviceErrorResponse, unauthorizedError, …
│   │   └── test/         mockUserRepository, mockRefreshTokenRepository, mockUsers
│   └── ws/               WebSocket server handlers
├── frontend/
│   ├── features/         Self-contained feature modules (login, topNav, websocket, analytics)
│   ├── layout/           PageLayout shell + layout constants
│   ├── pages/            Page-level components (HomePage, LoginPage, NotFoundPage, …)
│   ├── providers/        React providers (AuthProvider, ThemeProvider, ToastProvider)
│   ├── redux/
│   │   ├── api/          RTK Query API slices (baseApi, authApi, integrationsApi)
│   │   ├── middleware/   localStorageMiddleware — persists theme + auth to localStorage
│   │   └── slices/       themeSlice
│   ├── shared/           Reusable components (skeletons, ErrorBoundary, ProtectedRoute, …)
│   └── telemetry/        Optional browser OTel fetch instrumentation
├── e2e/
│   ├── api/              Playwright API tests (no browser)
│   └── frontend/         Playwright browser tests (Chromium)
├── rest/                 .http files for every endpoint (kulala.nvim / REST Client)
├── docs/                 Obsidian vault — ADRs + technical docs
└── docker/               Dockerfiles + service configs

Path Aliases

AliasResolves toUse when
@backend/*./backend/*Crossing into the backend from anywhere
@frontend/*./frontend/*Crossing into the frontend from anywhere
@type/*./types/*Shared types (used in both layers)

Relative imports are fine within the same layer. Never use ../../ across layer boundaries.

Dependency Injection

All controllers and services use factory functions. See ADR-003 for the full rationale.

userRepository  ──injected into──▶  createUserService(repo)
                                              │
userService     ──injected into──▶  createUserController(service)
                                              │
                              routes call userController.getUsers()

In tests: inject mockUserRepository into createUserService, inject the result into createUserController.

Optional Integrations

Both integrations are opt-in via environment variables. When the relevant env var is absent the module is a complete no-op — no SDK initialised, no overhead.

IntegrationEnv var triggerWhat it enables
OpenTelemetryOTEL_ENDPOINTBackend traces, metrics, structured logs via OTLP HTTP
OpenPanelBUN_PUBLIC_OPENPANEL_CLIENT_IDFrontend analytics and session replay
SMTP emailSMTP_HOSTTransactional email via Nodemailer

Key Commands

bun dev              # Dev server + HMR → http://localhost:3210
bun run build        # Production bundle → dist/
bun test             # Unit tests (backend + frontend)
bun run cc           # Full quality gate: test + lint + format + knip
bun run db:generate  # Generate migration SQL from schema changes
bun run db:migrate   # Apply migrations to the database
bun run db:seed      # Create initial admin user (idempotent)
bun run e2e:docker   # Full E2E suite in isolated Docker stack