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.