Guides development of Fastify Node.js backend servers and REST APIs using TypeScript or JavaScript. Use when building, configuring, or debugging a Fastify application — including defining routes, implementing plugins, setting up JSON Schema validation, handling errors, optimising performance, managing authentication, configuring CORS and security headers, integrating databases, working with WebSockets, and deploying to production. Covers the full Fastify request lifecycle (hooks, serialization, logging with Pino) and TypeScript integration via strip types. Trigger terms: Fastify, Node.js server, REST API, API routes, backend framework, fastify.config, server.ts, app.ts.
95
95%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Use @fastify/http-proxy for simple reverse proxy scenarios:
import Fastify from 'fastify';
import httpProxy from '@fastify/http-proxy';
const app = Fastify({ logger: true });
// Proxy all requests to /api/* to another service
app.register(httpProxy, {
upstream: 'http://backend-service:3001',
prefix: '/api',
rewritePrefix: '/v1',
http2: false,
});
// With authentication
app.register(httpProxy, {
upstream: 'http://internal-api:3002',
prefix: '/internal',
preHandler: async (request, reply) => {
// Verify authentication before proxying
if (!request.headers.authorization) {
reply.code(401).send({ error: 'Unauthorized' });
}
},
});
await app.listen({ port: 3000 });For more control over proxying, use @fastify/reply-from with reply.from():
import Fastify from 'fastify';
import replyFrom from '@fastify/reply-from';
const app = Fastify({ logger: true });
app.register(replyFrom, {
base: 'http://backend-service:3001',
http2: false,
});
// Proxy with request/response manipulation
app.get('/users/:id', async (request, reply) => {
const { id } = request.params;
return reply.from(`/api/users/${id}`, {
// Modify request before forwarding
rewriteRequestHeaders: (originalReq, headers) => ({
...headers,
'x-request-id': request.id,
'x-forwarded-for': request.ip,
}),
// Modify response before sending
onResponse: (request, reply, res) => {
reply.header('x-proxy', 'fastify');
reply.send(res);
},
});
});
// Conditional routing
app.all('/api/*', async (request, reply) => {
const upstream = selectUpstream(request);
return reply.from(request.url, {
base: upstream,
});
});
function selectUpstream(request) {
// Route to different backends based on request
if (request.headers['x-beta']) {
return 'http://beta-backend:3001';
}
return 'http://stable-backend:3001';
}Build an API gateway with multiple backends:
import Fastify from 'fastify';
import replyFrom from '@fastify/reply-from';
const app = Fastify({ logger: true });
// Configure multiple upstreams
const services = {
users: 'http://users-service:3001',
orders: 'http://orders-service:3002',
products: 'http://products-service:3003',
};
app.register(replyFrom);
// Route to user service
app.register(async function (fastify) {
fastify.all('/*', async (request, reply) => {
return reply.from(request.url.replace('/users', ''), {
base: services.users,
});
});
}, { prefix: '/users' });
// Route to orders service
app.register(async function (fastify) {
fastify.all('/*', async (request, reply) => {
return reply.from(request.url.replace('/orders', ''), {
base: services.orders,
});
});
}, { prefix: '/orders' });
// Route to products service
app.register(async function (fastify) {
fastify.all('/*', async (request, reply) => {
return reply.from(request.url.replace('/products', ''), {
base: services.products,
});
});
}, { prefix: '/products' });Handle request bodies when proxying:
app.post('/api/data', async (request, reply) => {
return reply.from('/data', {
body: request.body,
contentType: request.headers['content-type'],
});
});
// Stream large bodies
app.post('/upload', async (request, reply) => {
return reply.from('/upload', {
body: request.raw,
contentType: request.headers['content-type'],
});
});Handle upstream errors gracefully:
app.register(replyFrom, {
base: 'http://backend:3001',
// Called when upstream returns an error
onError: (reply, error) => {
reply.log.error({ err: error }, 'Proxy error');
reply.code(502).send({
error: 'Bad Gateway',
message: 'Upstream service unavailable',
});
},
});
// Custom error handling per route
app.get('/data', async (request, reply) => {
try {
return await reply.from('/data');
} catch (error) {
request.log.error({ err: error }, 'Failed to proxy request');
return reply.code(503).send({
error: 'Service Unavailable',
retryAfter: 30,
});
}
});Proxy WebSocket connections:
import Fastify from 'fastify';
import httpProxy from '@fastify/http-proxy';
const app = Fastify({ logger: true });
app.register(httpProxy, {
upstream: 'http://ws-backend:3001',
prefix: '/ws',
websocket: true,
});Configure proxy timeouts:
app.register(replyFrom, {
base: 'http://backend:3001',
http: {
requestOptions: {
timeout: 30000, // 30 seconds
},
},
});Add caching to proxied responses:
import { createCache } from 'async-cache-dedupe';
const cache = createCache({
ttl: 60,
storage: { type: 'memory' },
});
cache.define('proxyGet', async (url: string) => {
const response = await fetch(`http://backend:3001${url}`);
return response.json();
});
app.get('/cached/*', async (request, reply) => {
const data = await cache.proxyGet(request.url);
return data;
});