Fastify is one of the fastest Node.js web frameworks — but its plugin system, schema validation, and hooks make it far more powerful than a simple HTTP server.
Plugin Architecture
Fastify's encapsulated plugin system is its killer feature:
import Fastify from "fastify"
const app = Fastify({ logger: true })
// Register a plugin with encapsulated scope
app.register(async function userRoutes(fastify) {
// This decorator is only visible inside this plugin
fastify.decorate("userService", new UserService())
fastify.get("/users", async (request) => {
return fastify.userService.findAll(request.query)
})
fastify.get("/users/:id", async (request) => {
return fastify.userService.findById(request.params.id)
})
fastify.post("/users", async (request, reply) => {
const user = await fastify.userService.create(request.body)
reply.code(201).send(user)
})
}, { prefix: "/api/v1" })
await app.listen({ port: 3000 })
JSON Schema Validation
Fastify validates requests AND generates documentation:
const createUserSchema = {
body: {
type: "object",
required: ["name", "email"],
properties: {
name: { type: "string", minLength: 2 },
email: { type: "string", format: "email" },
age: { type: "integer", minimum: 0 }
}
},
response: {
201: {
type: "object",
properties: {
id: { type: "string" },
name: { type: "string" },
email: { type: "string" },
createdAt: { type: "string" }
}
}
}
}
app.post("/users", { schema: createUserSchema }, async (request, reply) => {
const user = await db.user.create({ data: request.body })
reply.code(201).send(user)
})
Hooks — Request Lifecycle
// Global hooks
app.addHook("onRequest", async (request) => {
request.startTime = Date.now()
})
app.addHook("onResponse", async (request, reply) => {
const duration = Date.now() - request.startTime
request.log.info({ duration, status: reply.statusCode }, "request completed")
})
// Auth hook
app.addHook("preHandler", async (request, reply) => {
if (request.url.startsWith("/api/protected")) {
const token = request.headers.authorization?.replace("Bearer ", "")
if (!token) {
reply.code(401).send({ error: "Unauthorized" })
return
}
request.user = await verifyToken(token)
}
})
Type Providers (TypeScript)
import Fastify from "fastify"
import { TypeBoxTypeProvider } from "@fastify/type-provider-typebox"
import { Type } from "@sinclair/typebox"
const app = Fastify().withTypeProvider<TypeBoxTypeProvider>()
app.post("/users", {
schema: {
body: Type.Object({
name: Type.String({ minLength: 2 }),
email: Type.String({ format: "email" })
}),
response: {
201: Type.Object({
id: Type.String(),
name: Type.String()
})
}
}
}, async (request) => {
// request.body is fully typed!
const { name, email } = request.body
return { id: "123", name }
})
WebSocket Support
import websocket from "@fastify/websocket"
await app.register(websocket)
app.get("/ws", { websocket: true }, (socket, request) => {
socket.on("message", (msg) => {
socket.send(JSON.stringify({ echo: msg.toString() }))
})
})
Key Takeaways
- Plugin system with encapsulation for clean architecture
- JSON Schema validation with auto-serialization (2x faster)
- Hooks for request lifecycle management
- Type providers for end-to-end TypeScript
- WebSocket support via plugins
- 20-50% faster than Express for JSON responses
Explore Fastify docs for the complete API.
Building web scrapers or data pipelines? Check out my Apify actors for ready-made solutions, or email spinov001@gmail.com for custom development.
Top comments (0)