NestJS vs Express: quan triar cadascun el 2026
3 de juliol del 2026 · SOM-OS
La pregunta continua apareixent en cada projecte: NestJS o Express? El 2026 la resposta no és “depèn” per peresa — és “depèn” perquè els dos marcs resolen problemes diferents. Express és una biblioteca HTTP minimalista. NestJS és un marc de treball amb convencions pròpies, arquitectura de classes, injecció de dependències i un ecosistema de mòduls. Triar malament surt car: refactoritzar un backend monolític en Express per fer-lo mantenible és feina de setmanes.
Aquesta comparativa té codi real, proves de rendiment mesurades i una matriu de decisió. Guarda-la com a referència. Si vols més context sobre la nostra pila tecnològica, mira apps aïllades vs sistemes.
Filosofia: minimalisme vs convenció
Express et dona app.get(), app.use() i poca cosa més. Tu decideixes l’estructura: on van els controladors, com es valida, com s’injecta. És llibertat absoluta — i l’origen de la majoria de backends impossibles de mantenir que arriben a les nostres mans. Cada equip estructura Express a la seva manera, i quan creix, creix en caos.
NestJS imposa arquitectura: controladors, serveis, mòduls, pipes, guards, interceptors. Si vens de Spring o Angular, resulta familiar. La corba inicial és més gran, però la convenció escala. Un projecte NestJS de 50 mòduls es pot recórrer. Un projecte Express de 50 fitxers solts, no.
El mateix endpoint en tots dos
Per veure’n la diferència, aquí tens un endpoint POST /users amb validació en cadascun.
Express 5:
import express from 'express';
const app = express();
app.use(express.json());
app.post('/users', async (req, res) => {
const { email, name } = req.body;
if (!email || !email.includes('@'))
return res.status(400).json({ error: 'email no vàlid' });
if (!name || name.length < 2)
return res.status(400).json({ error: 'name obligatori' });
const user = await db.user.create({ email, name });
res.status(201).json(user);
});
app.listen(3000);
Funciona. Però la validació és manual, no hi ha tipatge a req.body, no hi ha separació de capes. Quan hi afegeixes 20 endpoints més, això es converteix en una bola de fang.
NestJS 11:
import { Body, Controller, Post } from '@nestjs/common';
import { IsEmail, MinLength } from 'class-validator';
class CreateUserDto {
@IsEmail() email: string;
@MinLength(2) name: string;
}
@Controller('users')
export class UsersController {
constructor(private readonly users: UsersService) {}
@Post()
create(@Body() dto: CreateUserDto) {
return this.users.create(dto);
}
}
La validació la fa class-validator a través del ValidationPipe global. El DTO defineix el contracte. El controlador només delega en el servei. Si elimines el controlador, el servei continua sent comprovable amb proves. Aquesta separació és allò que manté un projecte fàcil de recórrer amb 200 endpoints.
Rendiment: proves reals
A mitjan 2026, ho mesuràrem amb autocannon -c 100 -d 30 en un Ubuntu 24.04 amb Node 22 LTS. Hello-world JSON en cada marc de treball:
- Express 5: ~38.000 req/s, latència P99 de 3.2ms.
- NestJS 11 (adaptador Express): ~34.000 req/s, P99 de 4.1ms.
- NestJS 11 (adaptador Fastify): ~52.000 req/s, P99 de 2.8ms.
- Fastify 5 (sense marc addicional): ~63.000 req/s, P99 de 2.1ms.
La sobrecàrrega de NestJS sobre Express és d’aproximadament un 10% en hello-world. Amb l’adaptador Fastify, NestJS supera Express sense marc addicional. En endpoints reals amb base de dades, la sobrecàrrega del marc desapareix dins el renou de la latència de la base de dades. Si tries Express per 4.000 req/s en un hello-world, optimitzes allò que no toca.
El coll d’ampolla d’un backend en producció gairebé mai és el marc de treball. És la base de dades, les crides externes i les cues. NestJS no et frena. T’organitza.
Injecció de dependències
NestJS du un contenidor d’injecció de dependències natiu. Registres un provider en un mòdul i l’injectes en qualsevol servei del mateix mòdul:
@Module({
providers: [UsersService, PrismaService],
controllers: [UsersController],
})
export class UsersModule {}
A Express has de muntar la teva pròpia injecció de dependències: typedi, awilix, o passar dependències a mà. Funciona, però és una solució casolana. NestJS ho resol de fàbrica, amb àmbit per mòdul i proves trivials — simules el provider i llestos.
Proves
NestJS hi destaca. El mòdul de proves simula dependències sense fricció:
const moduleRef = await Test.createTestingModule({
controllers: [UsersController],
providers: [
UsersService,
{ provide: PrismaService, useValue: mockPrisma },
],
}).compile();
const ctrl = moduleRef.get(UsersController);
expect(await ctrl.create({ email: 'a@b.c', name: 'Ana' }))
.toEqual({ id: 1, email: 'a@b.c', name: 'Ana' });
A Express, supertest + simulació manual. Funciona, però requereix disciplina. El problema no és que no es puguin fer proves amb Express — és que res t’obliga a fer-les bé.
Matriu de decisió
Això és allò que posam damunt la taula quan un client ens demana quina pila tecnològica ha de fer servir:
- Fes servir Express si: és un prototip de cap de setmana, un webhook simple, una API de menys de 10 endpoints sense lògica de negoci, o ja tens una base Express i migrar no compensa.
- Fes servir NestJS si: el backend creixerà més enllà de 15 endpoints, hi ha lògica de negoci amb múltiples serveis, necessites cues (BullMQ), websockets, o hi posaràs agents d’IA que requereixen orquestració.
- Fes servir NestJS + Fastify si: necessites el màxim rendiment i ja has superat la corba d’aprenentatge de NestJS. L’adaptador és una línia.
- No en facis servir cap si: necessites temps real pur amb milers de connexions — fes servir Bun + Hono o directament Go/Rust. Node no és sempre la resposta.
El cost real de triar malament
Hem heretat projectes Express de 8.000 línies sense ni una prova, sense separació de capes, amb validació a mà en cada ruta. La refactorització cap a una arquitectura modular va tardar tres setmanes. Si hagués començat amb NestJS, la refactorització no hauria estat necessària — la convenció hauria mantingut l’ordre des del primer dia.
El cost d’Express no és al dia 1. És al dia 400, quan l’equip ha crescut, hi ha cinc persones tocant el mateix fitxer i ningú sap on és la lògica de validació d’aquell endpoint que falla en producció a les 2am.
Per això a SOM-OS la nostra pila tecnològica per defecte és NestJS 11 + PostgreSQL + Redis + BullMQ + LangChain. No perquè Express sigui dolent — perquè els sistemes que construïm necessiten l’arquitectura que NestJS imposa. Si vols entendre per què aquesta arquitectura importa abans d’escriure una línia de codi, llegeix sobre el Mapa del Caos i el nostre enfocament d’apps aïllades vs sistemes.
Conclusió
Express és una eina excel·lent per a allò per al qual es va dissenyar: HTTP minimalista. El 2026 continua sent vàlid per a projectes petits i prototips. NestJS no el substitueix — el complementa. Quan un projecte creix, NestJS t’estalvia la refactorització que Express t’obligarà a fer tard o d’hora.
La regla que feim servir: si el projecte viu més de 6 mesos o té més de 2 desenvolupadors, NestJS. Si és un experiment que s’esborra dilluns, Express i endavant. La diferència no és de rendiment — és de mantenibilitat. I la mantenibilitat és allò que costa doblers reals, no els 4.000 req/s que perds en hello-world.
El marc de treball no fa que el codi sigui bo. Però un marc amb convencions fa més difícil escriure codi dolent. Això, a llarg termini, val més que qualsevol prova de rendiment.