Internative Logo

Choosing a Backend Stack: NestJS, Express or .NET?

Choosing a Backend Stack: NestJS, Express or .NET?

Choosing a Backend Stack: NestJS, Express or .NET?

Backend framework selection gets argued as a technical preference on most teams. In practice it is a team decision: it determines how long it takes an engineer who joins six months from now to become productive.

This article covers the stack we use and why: NestJS, pnpm and Turborepo on most projects, .NET on enterprise work. It also compares them honestly against Express.

The real cost of Express

Express is not a bad tool. It is minimal, fast, and takes about an hour to learn. For a solo developer, or a small service that does one thing, it can be the right call.

The problem starts at scale. Because Express imposes no structure, every project invents its own. Three projects end up with three folder layouts, three approaches to error handling and three ways of validating input.

That bill comes due in two places.

Onboarding time. A new engineer relearns the layout on every project. "Is there a service layer here, where are errors caught, where does validation happen?" has a different answer each time.

Handover cost. When you pass a project to another team, or return to it years later, you have to remember the convention you invented. If the documentation has gone stale, the code becomes the only source of truth.

This cost is invisible on one project and heavy across ten.

What NestJS solves

The value of NestJS is not its feature list. It is that the decisions are already made.

Module structure, dependency injection, layer separation, validation pipes and test setup all ship with the framework. An engineer looking at a NestJS project for the first time still knows where the controller is, where the service is, and how validation works.

We saw the payoff on the Sportez project: mobile app, backend and admin panel delivered in eight weeks. That pace comes from not relitigating the architecture each time.

Dependency injection also makes testing considerably easier. Swapping dependencies for test doubles becomes a natural operation rather than a trick bolted on afterwards.

It has a cost too. The learning curve is steeper than Express. Decorators, modules and providers feel abstract at first. For a small single-purpose service it can be unnecessarily heavy, and in serverless scenarios where cold start matters, the startup weight is noticeable.

pnpm and Turborepo: the invisible win

These two get overshadowed by the framework argument, but their daily impact is large.

pnpm stores each package once on disk and links it rather than copying. For a team running several projects on one machine, that is a meaningful saving in disk space and install time.

The more important side effect: pnpm's strict dependency tree stops you accidentally using a package you never declared. The phantom dependency problem, where code works on one machine and breaks on another, disappears.

Turborepo caches tasks. It does not rebuild a package that has not changed. In a repository holding several applications and shared libraries, build time drops from minutes to seconds.

Neither of these impresses anyone in isolation, but across commands that run dozens of times a week the cumulative effect is significant.

Why .NET on enterprise projects

On some projects we do not go near Node.js. With enterprise clients we mostly use .NET, and the reason is not a claim of technical superiority.

The client's ecosystem. Mid-sized and large companies typically run Microsoft-centred IT: Active Directory for identity, existing SQL Server installations, other systems on Azure. .NET sits naturally in that environment.

The internal team can take it over. If the client's own IT team already knows .NET, maintaining the system after handover is straightforward. We do not want them dependent on us.

Long-term support. Enterprise systems live a long time. .NET's release policy and backward compatibility make upgrading the system five years out predictable.

Audit and compliance. In regulated sectors, a mature toolchain and enterprise support options are an advantage.

This is not a language war. We could do the same work in either stack; what decides it is where the client will be in five years.

How we actually decide

Three questions are usually enough.

Which stack does the client's internal team know? If the team maintaining the system after handover sits on the Microsoft side, .NET. If not, the Node.js side moves faster.

How many services will this become? One service with a small scope, and Express may be sufficient. Multiple applications, shared libraries and a long life, and it is NestJS with a monorepo.

How large is the team and how much will it change? The larger and more fluid the team, the more the structure a framework imposes is worth. For one person working alone, flexibility may be worth more.

When we would pick Express

Honestly, we have not chosen it for a large project yet, but there are valid cases: a single-endpoint webhook receiver, a short-lived prototype, or a small addition to an existing Express codebase. In those situations the structure of NestJS is more burden than benefit.

The distinction that matters is how long the project will live. Building structure for something that lasts three months is waste; not building it for something that lasts three years is more expensive.

The full stack

On a typical project today: NestJS for the backend, pnpm for package management, Turborepo for the monorepo, MongoDB for the data layer, Next.js for the interface. On enterprise work the backend becomes .NET.

These choices share one principle: it should keep working when another team takes over. The codebase we deliver belongs to the client, and we do not want to leave behind a system only we can understand.

If you want to talk through these decisions on your own project, bring your current stack and the point that slows you down most.