Dependency injection (DI) is the mechanism that powers everything in NestJS, wiring controllers, services, and other providers together automatically. This lesson explains how Nest's DI container actually works.
How NestJS Resolves Dependencies
When Nest builds a module, it inspects every provider's constructor using TypeScript's emitted type metadata. For each constructor parameter, it looks up a matching provider (by class reference or injection token) and either reuses an existing instance or constructs a new one, recursively resolving that provider's own dependencies first.
This means you rarely instantiate classes with new in a Nest application, the framework's IoC (Inversion of Control) container does it for you based purely on constructor type signatures.
Nest resolves CatsRepository first, injects it into CatsService, then injects the resulting CatsService instance into CatsController, all without any manual wiring code.
NestJS favors constructor injection, dependencies are declared as typed constructor parameters, making them explicit, immutable (with readonly), and easy to spot when reading a class. Property injection with @Inject() on a class field exists but is used far less often, typically only to resolve circular dependencies.
Property injection combined with forwardRef() is one common way to break a circular dependency between two services that need each other.
Why DI Makes Testing Easier
Because dependencies are resolved by the container rather than hardcoded, unit tests can build a TestingModule and swap any provider for a mock or stub, no monkey-patching or module-mocking tricks required.
Common Mistakes
Forgetting @Inject(token) when a provider uses a custom string or symbol token.
Creating circular dependencies between two services without forwardRef().
Instantiating a service manually with new, bypassing the DI container and losing its own injected dependencies.
Confusing a class used purely as a TypeScript interface (erased at runtime) with an actual injectable provider.
Key Takeaways
Nest's IoC container resolves constructor dependencies automatically based on type or token.
Constructor injection is the standard pattern; property injection is mostly for edge cases like circular deps.
Custom injection tokens let you inject values, factories, or swappable interface implementations.
DI is what makes Nest's testing utilities able to swap real providers for mocks so easily.
Pro Tip
If you see a circular dependency error at startup, first ask whether the two services truly need each other. Often the fix is extracting shared logic into a third service rather than reaching for forwardRef().
You now understand how NestJS's dependency injection container works. Next, move into NestJS Routing to see how requests are matched to controller methods.