What are Singleton providers?
What a Singleton provider is
A Singleton provider is a provider with only one instance for the whole NestJS application. The first time NestJS's container creates that provider, it caches the instance, and every later request for that provider gets the same copy.
Example
javascript
import { Injectable } from '@nestjs/common';
@Injectable()
export class CounterService {
private counter = 0;
increment() {
this.counter++;
return this.counter;
}
}And two controllers use this service:
javascript
import { Controller, Get } from '@nestjs/common';
import { CounterService } from './counter.service';
@Controller('a')
export class AController {
constructor(private readonly counterService: CounterService) {}
@Get()
add() {
return this.counterService.increment();
}
}
@Controller('b')
export class BController {
constructor(private readonly counterService: CounterService) {}
@Get()
add() {
return this.counterService.increment();
}
}What happens:
- NestJS creates one shared instance of
CounterService; - both controllers (
AControllerandBController) use the same object; - the counter's value is shared:
| Request | Result |
|---|---|
GET /a → 1 | created for the first time |
GET /b → 2 | uses the same instance |
Why this is convenient
- Saves resources, the object is created only once.
- Shared state, handy for caching data, holding a connection pool, etc.
- Simplicity, no need to manually manage the object's lifetime.
How NestJS decides a provider is a Singleton
By default, every provider in NestJS has scope: DEFAULT,
and DEFAULT means singleton.
javascript
@Injectable({ scope: Scope.DEFAULT }) // the same as just @Injectable()
export class MyService {}Other kinds of scope
| Scope | Description |
|---|---|
| Singleton (DEFAULT) | One instance for the whole application (the default) |
| Request | A new instance for every HTTP request |
| Transient | A new instance for every injection (a fresh object every time) |
A Request-Scoped provider example:
javascript
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class RequestService {
constructor() {
console.log('A new instance for this request!');
}
}In this case, Nest creates a new object for every HTTP request, instead of reusing a shared one.
When a Singleton provider is a good idea
It's a great fit for:
- services with shared logic that doesn't depend on the request;
- logging, configuration, DB access (a single connection);
- caching, tokens, services that don't hold unique per-request data.
When a Singleton provider is a bad idea
Don't use a Singleton if:
- the provider holds state tied to a specific request;
- it needs to be isolated between users;
- it holds user data that can't be shared across requests.
In those cases, Scope.REQUEST or Scope.TRANSIENT is the right choice.
Summary
| Concept | Description |
|---|---|
| Singleton provider | One shared provider instance for the whole application |
| Created | Once, when the module initializes |
| Used | Everywhere it's injected |
| By default | Every NestJS provider is a singleton |
| Advantages | Faster, cheaper, convenient for caches, constants, the DB |
| Limitations | Not suited for dependencies that are unique per request |
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.