3 min read Emadideen Ghannam

Switching from .NET to NestJS on side projects (and what I'd switch back for)

Fourteen years of .NET, then NestJS for side projects. What I missed, what I gained, and the line where I'd still pick .NET for a paying client.

I have shipped .NET for 14 years. Government recruitment platforms, Supply Chain Management rebuilds, fleet platforms, and the B2B SaaS I lead at $work today. When I started a new side project recently my reflex was to scaffold a dotnet new webapi and an EF Core context.

I tried NestJS instead. Prisma for the schema. PostgreSQL. BullMQ for jobs. Same backend shape I would have built in .NET, different language and runtime.

This is what I missed about .NET, what NestJS genuinely improved, and the line where I’d still pick .NET for a paying client.

What I missed

EF Core migrations. EF Core’s migration story is the polished one. dotnet ef migrations add produces a migration file you can read, a Down() method that rolls back, and a model snapshot that catches drift. Prisma’s prisma migrate is simpler at the surface, but the SQL it emits is harder to review and there is no rollback. I have learned to write Prisma migrations more conservatively because I cannot trust an automatic rollback.

LINQ. I still miss LINQ. Where, Select, GroupBy, Join, with type-safe expressions that compile to SQL. Prisma’s query API is more limited and you fall through to raw SQL faster than you’d expect. TypeScript’s type system covers a lot, but .where({ AND: [{ x: { gt: 5 } }, { y: { lt: 10 } }] }) is not the same elegance as .Where(r => r.X > 5 && r.Y < 10).

The MediatR + FluentValidation pipeline. ASP.NET’s pipeline behaviour for cross-cutting concerns - validation, logging, transactions, caching - is cleaner than NestJS interceptors + class-validator + class-transformer. NestJS gets you there with more glue.

Boring structured logging. Serilog is boring. Boring is a feature in observability. The Node ecosystem still has three ways to do structured logging and none of them are obviously the right one in 2026.

What I gained

TypeScript end-to-end. This is the biggest one. The shared types in libs/contracts mean the SPA build fails before runtime when the API surface changes. With .NET + Angular I had to maintain the contract twice or run an OpenAPI generator and pretend it didn’t drift.

Prisma’s developer experience for greenfield. Schema-first. prisma generate produces types instantly. The studio gives you a passable admin UI. For a side project’s first three weeks, nothing in .NET is faster.

BullMQ instead of Hangfire’s quirks. Hangfire is fine. BullMQ is better at the things I actually need: priority, repeating jobs, delayed retries, a real dashboard, multi-process workers without licensing oddities.

Hot reload that actually works. nest start --watch reloads in under a second. dotnet watch works, but the second time around it has to recompile a lot more.

A side-by-side

The same endpoint - “create record, validate, write” - in both stacks.

ASP.NET Minimal API + EF Core:

app.MapPost("/attendance", async (AttendanceDto dto, AppDb db) => {
    var entity = new Attendance { EmployeeId = dto.EmployeeId, ClockedAt = dto.ClockedAt };
    db.Attendance.Add(entity);
    await db.SaveChangesAsync();
    return Results.Created($"/attendance/{entity.Id}", entity);
})
.AddEndpointFilter<ValidationFilter<AttendanceDto>>();

NestJS + Prisma:

@Controller('attendance')
export class AttendanceController {
  constructor(private prisma: PrismaService) {}

  @Post()
  async create(@Body() dto: CreateAttendanceDto) {
    return this.prisma.attendance.create({
      data: { employeeId: dto.employeeId, clockedAt: dto.clockedAt },
    });
  }
}

About the same length. The .NET version has an explicit validation pipeline. The NestJS version assumes a global ValidationPipe. Both work. Neither is meaningfully shorter.

Where I’d switch back

I’d pick .NET for a paying client if any of these are true:

  • Enterprise integration is the job. SAP, Oracle EBS, Microsoft AD, Dynamics, SQL Server stored procedures, BizTalk-shaped messaging. .NET has 25 years of library investment in those.
  • Regulated workloads. Banking, defence, healthcare. Microsoft’s compliance tooling and the .NET runtime’s audit story are easier conversations with auditors.
  • A large existing .NET team. Senior teams own their stack for 5 years. Forcing a .NET shop to adopt Node because a single architect liked it once is how you make slow misery.

For a solo greenfield SaaS aimed at SMB customers, none of those applied. NestJS won.


Edit on GitHub (opens in new tab)