DEV Community

Alex Spinov
Alex Spinov

Posted on

Fastify Has a Free API You Should Know About

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 })
Enter fullscreen mode Exit fullscreen mode

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)
})
Enter fullscreen mode Exit fullscreen mode

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)
  }
})
Enter fullscreen mode Exit fullscreen mode

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 }
})
Enter fullscreen mode Exit fullscreen mode

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() }))
  })
})
Enter fullscreen mode Exit fullscreen mode

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)