Nest does not implement HTTP. It drives an adapter, and two ship with it: @nestjs/platform-express (the default) and @nestjs/platform-fastify (over Fastify 5.12.5 314,116 here). Switching is one argument: NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter()). Every module, controller, guard, pipe and DTO here booted unchanged on both, and the headers give the swap away:
$ curl -sI localhost:4310/api/v1/books?limit=1 # Express adapter HTTP/1.1 200 OK X-Powered-By: Express content-type: application/json Keep-Alive: timeout=5 $ curl -sI localhost:4311/api/v1/books?limit=1 # Fastify adapter, same AppModule HTTP/1.1 200 OK X-Request-Id: 8e334b7c content-type: application/json Keep-Alive: timeout=72
The honest part is what does not port. Anything that touches the platform object leaks, silently, until it runs. Bookshelf's exception filter ends its catch with res.status(status).json(body) — correct on Express 24,430 , and on Fastify it produced this, because a Fastify reply has send, not json:
$ curl -s -X POST localhost:4311/api/v1/books -d '{}'
{"statusCode":500,"error":"Internal Server Error",
"message":"res.status(...).json is not a function"}A 401 became a 500, from the code that exists to prevent exactly that. The fix is res.send(body), or better, keeping @Res() and raw platform objects out of your code. The same trap catches multer-based uploads and any express-session 6,354 or passport package that expects Express's req/res prototypes.
So which one? Fastify's router and JSON serializer are faster. But Nest validates with class-validator rather than Fastify's schemas, so you get the server's speed without its best feature, and most middleware you would reach for is Express-shaped. Start on Express, and use Measuring Performance to measure whether the HTTP layer is your bottleneck — in an API that talks to MongoDB 1,815 , it usually is not.