All Node.js templates

Node.js + Fastify

Fastify with schemas, plugins, and high-performance JSON handling.

DevZone Tools1,320 copiesUpdated Feb 24, 2026Node.jsTypeScript
# 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