Skip to content
Go back

Fastify TypeScript API Development: High-Performance Server Framework

Fastify TypeScript API Development: High-Performance Server Framework

Fastify is a fast and low-overhead Node.js web framework. Combined with TypeScript, it provides a robust platform for building high-performance APIs. This guide covers setup, routing, schema validation, plugin development, and production best practices.

Why Choose Fastify with TypeScript?

Step 1: Project Setup and Configuration

Initialize a Fastify project with TypeScript and essential tooling:

# Create project directory
mkdir fastify-ts-api
cd fastify-ts-api

# Initialize package.json
npm init -y

# Install Fastify and TypeScript
npm install fastify fastify-plugin fastify-autoload
npm install -D typescript ts-node-dev @types/node

# Install validation and utilities
npm install fastify-sensible @fastify/jwt @fastify/cors
npm install zod
npm install mercurius

# Install development dependencies
npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
npm install -D prettier husky lint-staged

Configure TypeScript in tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["ES2020"],
    "module": "CommonJS",
    "rootDir": "src",
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

Step 2: Fastify Server Setup

Create src/server.ts:

import Fastify from "fastify";
import sensible from "fastify-sensible";
import cors from "@fastify/cors";
import jwt from "@fastify/jwt";
import autoload from "@fastify/autoload";
import path from "path";

export async function buildServer() {
  const fastify = Fastify({
    logger: { level: "info" },
  });

  // Register plugins
  fastify.register(sensible);
  fastify.register(cors, { origin: true });
  fastify.register(jwt, { secret: process.env.JWT_SECRET! });

  // Load routes and plugins automatically
  fastify.register(autoload, {
    dir: path.join(__dirname, "plugins"),
    options: { prefix: "/plugins" },
  });

  fastify.register(autoload, {
    dir: path.join(__dirname, "routes"),
    options: { prefix: "/api" },
  });

  return fastify;
}

// If run directly, start server
if (require.main === module) {
  buildServer()
    .then(fastify => fastify.listen({ port: 3000 }))
    .catch(err => {
      console.error(err);
      process.exit(1);
    });
}

Step 3: Route Definition and Validation

Use Zod and JSON schema for route validation. Example src/routes/users.ts:

import { FastifyPluginAsync } from "fastify";
import { z } from "zod";

const userSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  name: z.string().min(1),
});

const createUserBody = z.object({
  email: z.string().email(),
  name: z.string().min(1),
  password: z.string().min(8),
});

type CreateUserBody = z.infer<typeof createUserBody>;

const userRoutes: FastifyPluginAsync = async fastify => {
  fastify.get<{ Params: { id: string } }>(
    "/:id",
    {
      schema: {
        params: {
          type: "object",
          properties: { id: { type: "string", format: "uuid" } },
          required: ["id"],
        },
      },
    },
    async (request, reply) => {
      const { id } = request.params;
      const user = await fastify.prisma.user.findUnique({ where: { id } });
      if (!user) {
        return reply.notFound("User not found");
      }
      return user;
    }
  );

  fastify.post<{ Body: CreateUserBody }>(
    "/",
    {
      schema: {
        body: createUserBody.toJSON(),
      },
    },
    async (request, reply) => {
      const parsed = createUserBody.parse(request.body);
      const hash = await fastify.bcrypt.hash(parsed.password);

      const user = await fastify.prisma.user.create({
        data: { email: parsed.email, name: parsed.name, passwordHash: hash },
      });
      return reply.code(201).send(user);
    }
  );
};

export default userRoutes;

Step 4: Plugin Development

Example custom plugin src/plugins/logger.ts:

import fp from "fastify-plugin";

export default fp(async fastify => {
  fastify.addHook("onRequest", async request => {
    request.log.info(
      { method: request.method, url: request.url },
      "Incoming request"
    );
  });

  fastify.addHook("onResponse", async (request, reply) => {
    request.log.info({ statusCode: reply.statusCode }, "Response sent");
  });
});

Step 5: Authentication and Authorization

Use Fastify JWT and hooks. Example in src/routes/auth.ts:

import { FastifyPluginAsync } from "fastify";
import { z } from "zod";

const loginBody = z.object({ email: z.string().email(), password: z.string() });

type LoginBody = z.infer<typeof loginBody>;

const authRoutes: FastifyPluginAsync = async fastify => {
  fastify.post<{ Body: LoginBody }>("/login", async (request, reply) => {
    const { email, password } = loginBody.parse(request.body);
    const user = await fastify.prisma.user.findUnique({ where: { email } });
    if (!user) return reply.unauthorized();
    const ok = await fastify.bcrypt.compare(password, user.passwordHash);
    if (!ok) return reply.unauthorized();

    const token = fastify.jwt.sign({ userId: user.id });
    return { token };
  });

  fastify.get(
    "/profile",
    { preHandler: [fastify.authenticate] },
    async (request, reply) => {
      const { userId } = request.user as { userId: string };
      const user = await fastify.prisma.user.findUnique({
        where: { id: userId },
      });
      return user;
    }
  );
};

export default authRoutes;

Step 6: Production Best Practices

  1. Logging: Use Pino for structured logs
  2. Monitoring: Integrate Prometheus metrics via fastify-metrics
  3. Schema Validation: Use Zod + JSON schema for fast validation
  4. Cluster Mode: Run behind PM2 or Kubernetes with multiple instances
  5. Security: Enable HTTPS, helmet, and rate limiting
  6. CI/CD: Automate lint, test, and build pipelines
  7. Documentation: Generate OpenAPI docs with fastify-oas
  8. Graceful Shutdown: Handle SIGINT/SIGTERM to close DB connections

Development Commands

# Dev with auto reload
npx ts-node-dev --respawn src/server.ts

# Build
npm run build

# Start production server
node dist/server.js

# Run lint and tests
npm run lint
npm test

Your Fastify TypeScript API is now configured for high performance, type safety, and production readiness!


Share this post on:

Previous Post
Setting Up Fastify + PostgreSQL with Drizzle ORM
Next Post
n8n AI Automation: Integrating AI Workflows with TypeScript