Express vs Fastify vs NestJS: Choosing the Right Framework
Express, Fastify and NestJS solve different problems. Here is how we decide between them on real client projects, and the trade-offs that actually matter.
Almost every backend engagement we take on at STRCLI starts with the same question: which Node.js framework should we build on? The honest answer is that all three of the big names are production-ready, and the wrong choice will not sink your project. But each one pushes you towards a different way of working, and picking the one that fits your team saves months of friction later.
Express: the default for a reason
Express has been the de facto standard for over a decade. Its middleware model is simple to reason about, every hosting provider documents it first, and there is a package for almost anything. If your team already knows it, that familiarity is worth a great deal.
The downsides are structural rather than technical. Express gives you no opinion on project layout, validation, error handling or dependency wiring, so every codebase invents its own conventions. On small services that is freedom; on a service touched by ten developers over three years, it becomes archaeology. Express 5 finally handles rejected promises in handlers properly, which removes the most common footgun, but the framework itself remains deliberately minimal.
Fastify: performance with guard rails
Fastify is what we reach for on most new API projects. It is significantly faster than Express under load, but speed is rarely the deciding factor. The real win is that validation and serialisation are first-class: you declare a JSON Schema for each route, and Fastify validates input and speeds up output encoding using the same declaration.
fastify.post('/orders', {
schema: {
body: {
type: 'object',
required: ['sku', 'quantity'],
properties: {
sku: { type: 'string' },
quantity: { type: 'integer', minimum: 1 },
},
},
},
}, async (req, reply) => createOrder(req.body));The plugin system with encapsulation is the other differentiator. Plugins get their own scope, so a database decorator registered in one part of the app cannot leak into another. It takes a day to understand and then quietly prevents a whole class of coupling problems.
NestJS: structure for larger teams
NestJS is a full application framework in the Angular or Spring mould: modules, providers, decorators and a dependency injection container. That is a lot of ceremony for a three-endpoint service, and we would not recommend it there. But for a large team building a long-lived platform, the enforced structure is precisely the point. Two NestJS codebases from different companies look remarkably alike, which makes onboarding fast and code review consistent.
It is worth knowing that Nest runs on top of Express by default and can swap in Fastify as its HTTP adapter, so the choice is less either-or than it first appears.
How we actually decide
- Small service, experienced team, unusual requirements: Express, because nothing gets in your way.
- New JSON API where correctness and throughput matter: Fastify, for schema validation and encapsulated plugins.
- Large team, long-lived product, mixed experience levels: NestJS, for conventions that survive staff turnover.
- Existing Express codebase that works: leave it alone. Rewrites need a better justification than fashion.
One more practical note: whichever you pick, put your business logic in plain modules that do not import the framework. Handlers should parse input, call a function and shape the response. Do that and a future migration becomes a week of glue work rather than a rewrite.
There is no universally correct answer, only a correct answer for your team, your product and your hiring plans. Choose deliberately, write it down, and revisit the decision when the facts change rather than when a new benchmark does the rounds.
If you are weighing up a framework decision for a new build, we are always happy to talk it through — get in touch with STRCLI.