Fastify Basics
10 examples to build your first Fastify 5 API - 7 basic and 3 intermediate.
Prerequisites
mkdir fastify-api && cd fastify-api
npm init -y
npm pkg set type=module
npm install fastify@5
npm install -D typescript@5.6 tsxFor schema validation and plugin patterns, see JSON Schema Validation and Fastify Plugins.
Basic Examples
1. Hello World Server
import Fastify from "fastify";
const app = Fastify({ logger: true });
app.get("/", async () => {
return { message: "Hello from Fastify 5" };
});
await app.listen({ port: 3000, host: "0.0.0.0" });- Return objects from handlers; Fastify serializes to JSON automatically
logger: trueenables Pino with sensible defaults- Use
await app.listen()(Fastify 5 async listen)
2. Route with Schema
import Fastify from "fastify";
const app = Fastify();
app.get("/users/:id", {
schema: {
params: {
type: "object",
properties: { id: { type: "string", format: "uuid" } },
required: ["id"],
},
response: {
200: {
type: "object",
properties: { id: { type: "string" }, name: { type: "string" } },
},
},
},
}, async (req) => {
return { id: req.params.id, name: "Ada Lovelace" };
});- Schema validates request and compiles response serialization
- Invalid requests return 400 automatically
- See JSON Schema Validation for depth
3. POST with Body Schema
import Fastify from "fastify";
const app = Fastify();
app.post("/users", {
schema: {
body: {
type: "object",
required: ["name", "email"],
properties: {
name: { type: "string", minLength: 1 },
email: { type: "string", format: "email" },
},
},
},
}, async (req, reply) => {
const { name, email } = req.body as { name: string; email: string };
return reply.status(201).send({ id: "1", name, email });
});reply.status(201).send()for explicit status codes- Body is validated before the handler runs
- TypeScript: use schema-to-type tools or assertion at boundary
4. Plugin Encapsulation
import Fastify from "fastify";
const app = Fastify();
app.register(async function userRoutes(fastify) {
fastify.get("/", async () => [{ id: 1, name: "Ada" }]);
fastify.get("/:id", async (req) => ({ id: (req.params as { id: string }).id }));
}, { prefix: "/users" });registercreates an encapsulated scope- Decorators and hooks inside a plugin do not leak to siblings
prefixmounts all routes under/users
5. Lifecycle Hooks
import Fastify from "fastify";
const app = Fastify({ logger: true });
app.addHook("onRequest", async (req) => {
req.headers["x-request-start"] = String(Date.now());
});
app.addHook("onResponse", async (req, reply) => {
const start = Number(req.headers["x-request-start"]);
req.log.info({ duration: Date.now() - start, status: reply.statusCode });
});
app.get("/health", async () => ({ ok: true }));- Hooks run at defined lifecycle stages
onRequestruns before routing;preHandlerruns before the handler- Use hooks for logging, auth, and timing
6. Error Handling
import Fastify from "fastify";
const app = Fastify();
app.setErrorHandler((err, _req, reply) => {
const status = err.statusCode ?? 500;
reply.status(status).send({
error: status === 500 ? "Internal server error" : err.message,
});
});
app.get("/fail", async () => {
throw Object.assign(new Error("Not found"), { statusCode: 404 });
});setErrorHandleris the centralized error handler- Attach
statusCodeto errors for operational errors - Validation errors from schemas have
statusCode: 400
7. Logging with Pino
import Fastify from "fastify";
const app = Fastify({
logger: {
level: process.env.LOG_LEVEL ?? "info",
transport: process.env.NODE_ENV === "development"
? { target: "pino-pretty" }
: undefined,
},
});
app.get("/users", async (req) => {
req.log.info({ action: "list_users" });
return [];
});req.logis a child logger with request context- Production: JSON logs to stdout; ship to your log aggregator
- See Logging with Pino
Intermediate Examples
8. Breaking Encapsulation with fastify-plugin
import Fastify from "fastify";
import fp from "fastify-plugin";
const authPlugin = fp(async (fastify) => {
fastify.decorate("authenticate", async (req: { headers: { authorization?: string } }) => {
if (!req.headers.authorization) throw Object.assign(new Error("Unauthorized"), { statusCode: 401 });
});
});
const app = Fastify();
await app.register(authPlugin);
// authenticate is available in all scopes- Without
fastify-plugin, decorators are scoped to the registering plugin - Use
fpfor shared auth, database connections, and config
9. Testing with inject()
import Fastify from "fastify";
import { test } from "node:test";
import assert from "node:assert/strict";
const app = Fastify();
app.get("/health", async () => ({ ok: true }));
test("GET /health", async () => {
const res = await app.inject({ method: "GET", url: "/health" });
assert.equal(res.statusCode, 200);
assert.deepEqual(res.json(), { ok: true });
});inject()simulates HTTP without network I/O- Orders of magnitude faster than supertest with port binding
- See Testing Fastify Apps
10. Server Timeout Tuning
import Fastify from "fastify";
const app = Fastify();
await app.listen({ port: 3000 });
const server = app.server;
server.keepAliveTimeout = 65_000;
server.headersTimeout = 66_000;
server.requestTimeout = 30_000;- Access underlying Node server via
app.serverafter listen - Align with reverse proxy timeouts
- See Keep-Alive & Connection Limits
FAQs
Is Fastify compatible with Express middleware?
Via @fastify/express or middie, but prefer native Fastify plugins. Express middleware loses schema validation benefits.
Fastify vs Express for new projects?
Fastify for performance and schema-first APIs. Express for maximum ecosystem and team familiarity. See Fastify vs Express ADR.
Does Fastify support ESM?
Yes. Fastify 5 is ESM-first. Use import Fastify from "fastify".
How do I handle TypeScript types for schemas?
Use fastify-type-provider-zod or @fastify/type-provider-typebox for inferred types from schemas.
Can NestJS use Fastify?
Yes. NestFactory.create(AppModule, new FastifyAdapter()). See NestJS Basics.
Related
- JSON Schema Validation - request/response schemas
- Fastify Plugins - autoload and encapsulation
- Logging with Pino - structured logging
- Express Basics - alternative framework
- Fastify Best Practices - section checklist
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.