Does Fastify Work With Strapi?
Fastify and Strapi can work together, but not as a direct integration—Fastify consumes Strapi's REST/GraphQL APIs rather than replacing its server.
Quick Facts
How Fastify Works With Strapi
Fastify and Strapi are not designed to integrate directly because Strapi is a complete headless CMS with its own Express-based server, while Fastify is a standalone web framework. The practical approach is to run Strapi as a separate backend service and use Fastify as your frontend API layer or custom business logic server that consumes Strapi's REST or GraphQL endpoints.
In this architecture, Fastify acts as a lightweight reverse proxy, middleware layer, or BFF (Backend-for-Frontend) that handles authentication, request transformation, caching, and custom endpoints before delegating to Strapi. This separation of concerns gives you Strapi's CMS capabilities without being locked into its performance characteristics, and lets you leverage Fastify's speed for high-traffic scenarios.
The developer experience is straightforward: start both servers independently, then use Fastify's http client or node-fetch to query Strapi's APIs. You won't get hot module reloading or unified debugging across the two, but you gain deployment flexibility and can scale them independently.
Best Use Cases
Fastify BFF proxying Strapi content with caching
npm install fastify @fastify/caching strapiimport Fastify from 'fastify';
import FastifyCache from '@fastify/caching';
import fetch from 'node-fetch';
const fastify = Fastify();
const STRAPI_URL = process.env.STRAPI_URL || 'http://localhost:1337';
await fastify.register(FastifyCache, { privacy: 'private' });
fastify.get('/api/articles', async (request, reply) => {
try {
const response = await fetch(`${STRAPI_URL}/api/articles`);
const data = await response.json();
reply.cache({ ttl: 300 }); // Cache 5 minutes
return data;
} catch (error) {
reply.code(502).send({ error: 'Strapi unavailable' });
}
});
fastify.post('/api/articles/publish', async (request, reply) => {
// Custom business logic before delegating to Strapi
const { title, content } = request.body;
const response = await fetch(`${STRAPI_URL}/api/articles`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ data: { title, content } })
});
return response.json();
});
await fastify.listen({ port: 3000 });Known Issues & Gotchas
Strapi's default Express server runs on port 1337; Fastify typically uses 3000, requiring explicit coordination
Fix: Configure both servers with distinct ports in .env files and ensure your deployment orchestration (Docker, PM2, K8s) starts both services
Authentication tokens from Strapi's JWT system must be manually validated in Fastify if you want unified auth
Fix: Store Strapi's JWT secret in your Fastify config and verify tokens using @fastify/jwt, or implement a shared session store like Redis
Strapi webhooks fire asynchronously and may not reach Fastify if network connectivity is poor between containers
Fix: Implement retry logic, use a message queue (RabbitMQ, Redis) between Strapi and Fastify, or poll Strapi's API at intervals
CORS issues arise when browser apps call both servers directly; Strapi CORS config won't cover Fastify routes
Fix: Either proxy all requests through Fastify (single origin), or configure CORS on both servers with matching allowed origins
Alternatives
- •Next.js (API routes) + Strapi: Next.js handles both frontend and API layer with built-in Strapi integration, trading Fastify's raw speed for developer ergonomics
- •Express.js + Strapi: Strapi itself uses Express internally, giving unified debugging and middleware sharing at the cost of Fastify's performance advantages
- •GraphQL Apollo Server + Strapi: If you want a unified GraphQL API layer above Strapi's REST endpoints with type safety
Resources
Related Compatibility Guides
Explore more compatibility guides