Node.js + Fastify
Fastify with schemas, plugins, and high-performance JSON handling.
# CLAUDE.md — Node.js + Fastify
## Why Fastify
- Schema-first request validation (uses JSON Schema or **TypeBox** / **Zod via plugin**).
- Faster than Express on JSON-heavy workloads.
- Native plugin system with explicit encapsulation — better than ad-hoc Express middleware ordering.
## Setup
- Node 20+. ESM (`"type": "module"`).
- TypeScript with `@fastify/type-provider-typebox` (or `@fastify/type-provider-zod`) for schema-driven types.
- Each plugin is a function that takes the `fastify` instance.
## Project layout
```
src/
app.ts # builds and decorates the fastify instance
server.ts # entry point
plugins/ # auth, db, swagger, etc.
routes/
users.ts # one route file per resource
schemas/ # TypeBox / Zod definitions
services/
```
## Schemas
- Every route declares `schema: { body, params, querystring, response }`. Don't ship a route without a response schema — it's a perf and correctness win.
- TypeBox example:
```ts
const UserParams = Type.Object({ id: Type.String({ format: "uuid" }) })
fastify.get("/users/:id", { schema: { params: UserParams } }, async (req) => { ... })
```
- Validation runs at the boundary; the handler gets typed inputs.
## Plugins
- One plugin per concern. `db.ts`, `auth.ts`, `metrics.ts`.
- Use `fastify-plugin` for plugins that decorate the root instance — encapsulation breaks otherwise.
- Lifecycle hooks (`onRequest`, `preHandler`, `onResponse`) are local to a plugin/encapsulated scope. Use that on purpose.
## Errors
- `app.setErrorHandler(...)` once. Map known errors to status codes.
- Throw `app.httpErrors.notFound("user")` etc. — Fastify ships an HTTP errors helper.
- Validation errors are 400 by default; customize the message format with a `schemaErrorFormatter`.
## Logging
- Fastify ships **pino**. Don't add a separate logger.
- `request.log` for per-request context. `app.log` for app-level.
- Add `app.addHook("onResponse", (req, reply) => req.log.info({ status: reply.statusCode }))` if you want structured access logs.
## Auth
- Use **@fastify/jwt** for JWT, **@fastify/oauth2** for OAuth flows.
- Auth check via `preHandler` hook on a route or scope. Don't duplicate per route.
## Performance
- Set `logger.level: "info"` in production; `"debug"` only when diagnosing.
- Enable HTTP/2 only when you have a real reason. It's not a free win.
- Use `fastify-compress` only after profiling — gzip CPU isn't free.
## Don't
- Don't add Express-style `app.use(middleware)`. Use Fastify hooks.
- Don't return raw entity objects without a response schema. Fastify's serializer can't optimize what it can't see.
- Don't share state via globals. Decorate the instance: `app.decorate("db", db)`.
- Don't call `app.listen` from `app.ts`. That belongs in `server.ts`.
Other Node.js templates
TypeScript Node.js Backend
Production Node.js in TypeScript: build, run, and deploy patterns.
Node.js + Express + TypeScript
Express in TypeScript: middleware, error handling, validation, structure.
Node.js + Prisma + Postgres
Prisma ORM patterns: schema design, migrations, raw queries, transactions.
Node.js + NestJS
NestJS modules, providers, and the dependency-injection conventions.
Node.js Microservice Patterns
Service boundaries, event-driven communication, retries, idempotency.