The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Chassis listing page.
A lightweight, decorator-driven Express + TypeScript backend starter. Clone, run, ship.
📖 Documentation · Getting started · create-chassis on npm
Chassis gives you NestJS-style controller ergonomics on plain Express 5 — in a handful of small files you can actually read. Zero configuration required: the server boots standalone, and every integration switches on only when you add its environment variable. Scaffold with a preset or pick à la carte — a database (Mongo, Postgres, or SQLite, ORM included), an auth provider (Auth0, Clerk, or built-in local sign-in), an optional Next.js front end, Sentry, an MCP server, and x402 payments — and the CLI ships only what you chose.
Export the class from src/controllers/index.ts — that's the whole wiring.
Or use the template directly:
That's it — no database, no env file, no accounts needed. Open http://localhost:8000/status.
New here? Follow the step-by-step getting-started guide — zero to a tested API in ~10 minutes.
Every path is non-interactive: --yes and --bare never prompt, and the CLI
skips prompts automatically whenever stdin isn't a TTY. One command produces a
project that already typechecks, lints and tests green.
Generated projects carry AGENTS.md, CLAUDE.md, llms.txt and an
add-resource skill, so whichever agent opens one writes code that matches the
rest of the codebase rather than fighting it.
@route / @protectedRoute on controller methods, controllers auto-mountreq.resHandler.ok() / .notFound() / .validation() with structured loggingcallId (or propagates x-call-id), echoed in responses and logssrc/config; the app refuses to boot on bad configvalidate({ body, query, params }) middleware with structured 400spackage.json carries only what you chose@paidRoute('get', '/report', '$0.01') via the x402 protocol (opt-in)--web adds an App Router app and makes the project an npm-workspaces monorepo (apps/api + apps/web); the auth provider you picked is wired on both sidesnpm run mcp, opt-in)/healthz (liveness) and /readyz (readiness, checks enabled integrations)npm run gen user scaffolds a controller + test wired to your ORM (Drizzle or Mongoose)AGENTS.md, CLAUDE.md, llms.txt, and an add-resource skill so agents write code that matches the conventions (see below)Most people scaffolding a backend today have an AI agent in the loop. Chassis is built so that agent-written code reads like hand-written code — because the framework gives agents rails and a verifiable finish line:
AGENTS.md + CLAUDE.md ship in every project — Claude Code, Cursor, Copilot, and Codex pick them up automatically and follow the conventions (thin controllers, resHandler responses, throw AppError, config in one place).npm run verify (strict TypeScript + ESLint + tests) is a deterministic quality gate agents iterate against until green..claude/skills/add-resource turns "add a books resource" into one consistent, checklisted operation.llms.txt gives doc-fetching tools a compact map of the conventions.Nothing to install — it's all in the scaffold. See AGENTS.md.
| Command | What it does |
|---|---|
npm run dev | Start with hot reload (tsx watch) |
npm test / npm run test:watch | Run the vitest suite |
npm run verify | Typecheck + lint + test (CI runs this) |
npm run build / npm start | Compile to dist/ and run production build |
npm run gen <Name> | Generate a controller + test |
npm run lint / npm run format | ESLint / Prettier |
Copy .env.example to .env. Each integration turns on when its variables are set — and stays completely dormant otherwise:
| Integration | Enable by setting | What you get |
|---|---|---|
| MongoDB | MONGODB_URI | Mongoose connection, readiness check, graceful disconnect |
| Auth0 | AUTH0_DOMAIN + AUTH0_AUDIENCE | JWT verification on every @protectedRoute |
| Sentry | SENTRY_DSN | Automatic error reporting from the central error handler |
Using a different IdP? Call setAuthProvider([...yourMiddleware]) at boot and @protectedRoute uses it — see src/core/auth.ts.
Local sign-in ships in three variants — emailed link, the classic credential
form, or both. Run npm create chassis --help to see the --auth values, or
read Authentication. Whichever you pick, they
share one session layer.
Four things worth knowing about the emailed-link flow:
GET never spends a token. Mail security scanners prefetch links, and a
single-use token burned by a scanner is how this feature usually breaks in
production. Redemption is a POST, on a click.SESSION_IDLE, hard SESSION_ABSOLUTE cap.| Variable | Default |
|---|---|
JWT_SECRET | (required) |
SESSION_IDLE / SESSION_ABSOLUTE | 30d / 90d |
MAGIC_TOKEN_TTL / MAGIC_CODE_ATTEMPTS | 15m / 5 |
MAGIC_LINK_BASE_URL | http://localhost:8000 |
SMTP_URL | unset → logs the email |
Chassis binds no email or SMS provider — bind yours through setMailTransport()
or setSmsTransport(). Proving an address fires one hook, setOnVerified(),
and that is the whole extension surface: consent and onboarding are yours.
Guides: magic link · sessions · transports
Read them at dvd90.github.io/chassis —
searchable, one page. The source lives in docs/ and the site
is generated from it, so the two can never disagree: