When I first discovered Nest.js, it felt like a promising way to bring order and enterprise-level patterns into the Node.js world. Its use of TypeScript, decorators, modules, and Dependency Injection looked compelling, almost like a Java-inspired ecosystem for JavaScript.
At first, that structure was attractive. It gave teams familiar concepts, a clear place for controllers and services, and a framework that seemed ready for serious backend systems.
But after working on multiple projects with it, I realized that the complexity can often outweigh the benefits. In 2025, I made a decisive move away from Nest.js and started using Fastify together with Domain Driven Design principles.
This is not a universal claim that Nest.js is bad. It is my personal experience with the tradeoffs, and why Fastify plus DDD ended up fitting my workflow better.
Where Nest.js Fell Short for Me
Nest.js can be productive, especially when the team already likes its conventions. The problem for me was that the framework often added more ceremony than I wanted in a TypeScript backend.
Too Many Layers and Abstractions
Nest.js borrows many ideas from Java's Spring framework: dependency injection containers, decorators, modules, providers, services, guards, interceptors, and more.
Those concepts can be useful, but in TypeScript and JavaScript many of them can also be implemented in simpler ways. Sometimes exporting a function, object, or instance from a module is enough.
With Nest.js, I often felt that I was writing code for the framework first and for the application second. The structure was consistent, but it also introduced several layers of indirection before a request reached the business logic.
That extra "magic" became noticeable during maintenance.
Debugging Became Slower Than It Needed to Be
While developing APIs, I encountered stack traces pointing into deeply wrapped compiled JavaScript files. Tracing an error back to the original TypeScript source could become tedious.
The issue was not that debugging was impossible. The issue was that it often required more context than it should have. Decorators, wrappers, generated metadata, and framework lifecycle hooks made the path from error to root cause longer.
When I needed a quick fix or wanted to understand a production issue, that friction mattered.
Version Conflicts and Dependency Noise
The Node.js ecosystem moves fast. One minor library update can break compatibility with another package, especially when TypeScript definitions shift.
I faced situations where Nest.js stopped building after a minor version change in a secondary dependency. Sometimes the runtime logic was fine, but types, decorators, or framework-specific packages disagreed with each other.
Rolling back versions or hunting for workarounds took time and energy I would rather spend on product logic.
This is one of the main reasons I started looking for a leaner option.
Why Fastify Worked Better for Me
Moving to Fastify felt like a breath of fresh air. It provides the essential HTTP server functionality without forcing a large architectural model on top of the application.
Fastify is fast, lightweight, and explicit. It gives me routing, plugins, schema validation, hooks, and a strong ecosystem, but it does not require me to structure the entire application around decorators or a dependency injection container.
When combined with TypeScript and DDD, I still get a clean architecture, but with fewer framework-imposed layers.
Clear Structure Without Framework Weight
The structure I prefer is simple:
- controllers for HTTP request and response handling,
- services for application and business use cases,
- repositories for data access,
- domain objects and value objects for core business rules,
- infrastructure adapters for external systems.
This keeps the architecture organized without making the framework the center of the codebase.
In a Fastify project, a controller can be just a route handler that calls an application service. A repository can be a normal TypeScript module or class. A domain service can stay framework-agnostic.
That separation is important because the business logic should not depend on the HTTP framework.
Simplified Debugging
With fewer decorators and wrappers, errors usually point more directly to the relevant TypeScript file.
The request flow is easier to follow:
- Fastify route receives the request.
- Controller validates or maps input.
- Service executes the use case.
- Repository or adapter handles persistence or external calls.
- Controller returns the response.
There is less hidden machinery in the middle. For my work style, that makes debugging and refactoring easier.
Fewer Dependency Headaches
Fastify itself is lightweight, and updates generally feel easier to manage. A smaller framework surface means fewer places where version conflicts can appear.
That does not mean Fastify projects have no dependencies. They do. But the dependency graph tends to feel more under control because the architecture is not built around a large framework ecosystem.
For small and mid-sized services, this reduced overhead matters.
Why I Still Rely on DDD
Leaving Nest.js did not mean abandoning structure. I still rely heavily on Domain Driven Design.
DDD is not about using a fancy framework. It is about designing software around core business logic. The most important parts of the system should express the business language, not framework internals.
Even after leaving Nest.js, my backend projects still keep a DDD-friendly layout:
- repositories for data access,
- services for business workflows,
- domain models for core rules,
- controllers for API endpoints,
- adapters for infrastructure concerns.
The difference is that I am no longer carrying the overhead that Nest.js introduced for my use cases.
When Nest.js Still Makes Sense
I do not think Nest.js is useless. It can be a good fit for teams that want a full-featured enterprise framework, especially if they come from Java, Spring, C#, or Angular-style architecture.
It can also help when a large team needs strict conventions and consistent patterns across many services.
For some organizations, the tradeoff is worth it.
But for teams that value flexibility, lean code, and direct control over application architecture, Fastify with DDD can be a better fit.
My Takeaway
In 2025, I realized that Nest.js was not the right default for me anymore. It gave structure, but it also introduced complexity, debugging friction, and dependency overhead that I did not want in every backend project.
Fastify gave me the HTTP foundation I needed, while DDD gave me the architecture I wanted.
That combination keeps the code explicit, testable, and easier to reason about. It also lets the domain model stay independent from the framework.
For my backend work, that has been the better tradeoff.


