How Nest Wires Your Objects Together
The Dependency Injection Container
Nest's DI container constructs your providers and wires them via constructor injection, so you never write `new` for your services.
What you'll learn
- Understand what dependency injection does for you
- Use constructor injection in providers and controllers
- Distinguish between class references and string tokens
Dependency injection (DI) is the heart of Nest. Instead of building objects yourself, you describe what you need; the framework constructs and hands them to you.
What DI Actually Does
Without DI, wiring an app means manually new-ing every dependency in the
right order:
// Manual wiring — fine for tiny apps, painful at scale
const repo = new UsersRepository(db);
const mailer = new Mailer(smtpConfig);
const service = new UsersService(repo, mailer);
const controller = new UsersController(service); With Nest, you declare classes as @Injectable() and ask for them in your
constructor. The container inspects the metadata, builds a dependency graph,
and constructs everything for you.
@Injectable()
export class UsersService {
constructor(
private readonly repo: UsersRepository,
private readonly mailer: Mailer,
) {}
} Constructor Injection
Nest only supports constructor injection for required dependencies.
TypeScript’s private readonly shortcut declares the field and assigns it
in one go.
@Controller('users')
export class UsersController {
// `users` is now a field on the instance, set by Nest
constructor(private readonly users: UsersService) {}
@Get()
list() {
return this.users.findAll();
}
} You don’t call new UsersService() anywhere. The module registers it; Nest
takes care of the rest.
Tokens vs Class References
Internally, every provider is keyed by a token. When you write
UsersService as the type, the class itself is the token — Nest reads
its constructor types via emitDecoratorMetadata and looks each up.
For non-class values (strings, plain config, interfaces) you need an explicit token, usually a string or symbol:
// Provider definition (in a module)
providers: [{ provide: 'API_KEY', useValue: process.env.API_KEY }];
// Injecting it — types alone can't identify a string, so you tell Nest
@Injectable()
export class StripeService {
constructor(@Inject('API_KEY') private readonly key: string) {}
} By-class is just a shortcut. Under the hood it’s the same lookup — a token maps to a provider definition, and the container builds it.
Services — The Business Layer →