Microservices With Fastify

Internal HTTP, Tiny Plugins, Shared Schemas

Microservices With Fastify

Fastify's plugin model scales out as well as up: each service is its own Fastify app, communicating over HTTP or messaging, with shared schemas for type-safety.

5 min read Level 3/5 #fastify#microservices#architecture
What you'll learn
  • Define service boundaries
  • Share TypeBox or zod schemas via a contracts package
  • Communicate over undici, NATS, or RabbitMQ

A Fastify app is small. So if your domain has clearly separable pieces — billing, search, notifications — each becomes its own service. Each is a Fastify process; together they form your platform.

Shared Contracts

The risky part of microservices is drift between services. Share schemas through a small contracts package consumed by everyone.

// contracts/src/user.ts
import { Type, type Static } from '@sinclair/typebox'

export const User = Type.Object({
  id: Type.String({ format: 'uuid' }),
  email: Type.String({ format: 'email' }),
})

export type User = Static<typeof User>

The users service uses User as its response schema; the billing service imports the same User for type-safe consumption.

Internal HTTP

For request/response, use HTTP between services. Pool connections with undici and add retries.

import { Pool } from 'undici'

const users = new Pool('http://users:8080', { connections: 20 })

export async function getUser(id: string): Promise<User> {
  const { body, statusCode } = await users.request({ path: `/users/${id}`, method: 'GET' })
  if (statusCode !== 200) throw new Error('users service ' + statusCode)
  return await body.json() as User
}

Async Messaging

For events (“user created”), use a broker — NATS, RabbitMQ, or Kafka. Producers publish, consumers subscribe; services stay decoupled.

import { connect } from 'nats'

const nc = await connect({ servers: app.config.NATS_URL })
app.decorate('nats', nc)

app.post('/users', async (req) => {
  const user = await app.db.user.create({ data: req.body })
  nc.publish('user.created', JSON.stringify(user))
  return user
})

A Gateway

Front the services with a small Fastify gateway that handles auth, rate limiting, and @fastify/http-proxy routes to internal hosts. Clients see one API; you keep service boundaries clean.

Testing With app.inject →