Express 24,430 has no opinion about where your code lives. That is its best feature on day one and its worst on day four hundred, when six developers have invented six ways to validate a body. Nest replaces "wherever you put it" with a fixed vocabulary — module, controller, provider, pipe, guard, interceptor, filter — and a container that assembles them. The structure costs something up front and only pays back at a certain size.
| Nest earns its keep when | Express stays the better choice when |
|---|---|
| Three or more developers share the codebase | One or two people own everything |
| The API will live for years and grow modules | The service is small and stable |
| TypeScript, DI and unit tests are already standard | The project is plain JavaScript |
| You want OpenAPI generated from the code | A few routes need no published contract |
| Queues, WebSockets, cron and gRPC live together | It is one HTTP surface and nothing else |
Three costs are real. A build step: nest build plus node dist/main.js is a slower loop than node --watch src/server.js. Indirection: learning what a request does means reading a controller, a guard, a pipe and a module, not one file top to bottom. Framework knowledge: a new hire learns Nest's rules on top of HTTP and Express before being useful.
What you get back is equally concrete. Every cross-cutting concern has one place to live, dependencies arrive through constructors so a service is tested with a stub in three lines, and the decorators that route a request also generate the OpenAPI document. A useful test before adopting it: name the three files a new endpoint touches in your Express project. If the honest answer is "it depends which developer wrote the neighboring route", Nest solves a problem you have.