Bird
Raised Fist0
NestJSframework~15 mins

Provider scope (default, request, transient) in NestJS - Deep Dive

Choose your learning style10 modes available
Overview - Provider scope (default, request, transient)
What is it?
In NestJS, provider scope controls how and when instances of services or classes are created and shared. There are three main scopes: default (singleton), request, and transient. Default scope means one instance is shared across the whole app. Request scope creates a new instance for each incoming request. Transient scope creates a new instance every time the provider is injected.
Why it matters
Provider scopes help manage resource usage and data isolation in applications. Without scopes, all services would be singletons, which can cause bugs when state is shared unexpectedly or when you need fresh data per request. Scopes allow developers to control lifecycle and memory, improving app reliability and performance.
Where it fits
Before learning provider scopes, you should understand basic NestJS providers and dependency injection. After mastering scopes, you can explore advanced topics like custom providers, lifecycle hooks, and request context management.
Mental Model
Core Idea
Provider scope defines how often and when NestJS creates new instances of a service to control sharing and isolation.
Think of it like...
Imagine a coffee machine in an office: default scope is like one shared coffee pot everyone drinks from; request scope is like making a fresh cup for each visitor; transient scope is like making a new cup every time someone asks, even if it’s the same person.
┌───────────────┐       ┌───────────────┐       ┌───────────────┐
│ Default Scope │──────▶│ Request Scope │──────▶│ Transient Scope│
│ (Singleton)   │       │ (Per Request) │       │ (Every Inject)│
└───────────────┘       └───────────────┘       └───────────────┘
Build-Up - 7 Steps
1
FoundationWhat is a Provider in NestJS
🤔
Concept: Introduces the idea of providers as classes or services that NestJS creates and injects where needed.
In NestJS, a provider is a class annotated with @Injectable() that can be injected into other classes. Providers hold business logic or data access code. By default, NestJS creates one instance of each provider and shares it across the app.
Result
You understand that providers are the building blocks of NestJS apps and that by default, one instance is shared everywhere.
Understanding providers as shared instances is key to grasping why scopes matter for controlling instance creation.
2
FoundationDefault Scope: Singleton Providers
🤔
Concept: Explains the default behavior where one instance of a provider is reused throughout the app.
When you create a provider without specifying scope, NestJS treats it as a singleton. This means the same instance is injected everywhere. This is efficient and works well for stateless services or shared resources.
Result
Your app uses one instance of the provider, saving memory and ensuring consistent state if needed.
Knowing that default scope means singleton helps you predict when state is shared and when it might cause issues.
3
IntermediateRequest Scope: New Instance Per Request
🤔Before reading on: do you think request scope creates one instance per user session or per HTTP request? Commit to your answer.
Concept: Request scope creates a new provider instance for each incoming HTTP request, isolating data per request.
By setting scope: Scope.REQUEST on a provider, NestJS creates a fresh instance for every HTTP request. This is useful when providers hold request-specific data, like user info or request metadata, preventing data leaks between requests.
Result
Each HTTP request gets its own provider instance, isolating state and improving safety.
Understanding request scope prevents bugs caused by shared mutable state in web apps handling many users.
4
IntermediateTransient Scope: New Instance Every Injection
🤔Before reading on: does transient scope create one instance per request or per injection? Commit to your answer.
Concept: Transient scope creates a new instance every time the provider is injected, even multiple times in the same request.
When a provider is marked with scope: Scope.TRANSIENT, NestJS does not reuse instances. Every injection point gets a fresh instance. This is useful for lightweight, stateless services or when you want complete isolation even within the same request.
Result
Multiple injections of the same provider produce separate instances, avoiding any shared state.
Knowing transient scope helps design services that must never share state, even within a single request.
5
IntermediateHow to Set Provider Scope in NestJS
🤔
Concept: Shows the syntax and decorator usage to define provider scopes.
You set scope by adding the 'scope' property in the @Injectable() decorator: @Injectable({ scope: Scope.REQUEST }) export class MyService {} or @Injectable({ scope: Scope.TRANSIENT }) export class MyService {} Default scope is used if omitted.
Result
You can control provider lifecycle explicitly by setting scope in the decorator.
Knowing the syntax empowers you to apply scopes correctly and avoid accidental singleton sharing.
6
AdvancedImpact of Scopes on Dependency Injection Tree
🤔Before reading on: do you think a transient provider injected into a singleton stays transient or becomes singleton? Commit to your answer.
Concept: Explains how scopes affect nested dependencies and instance sharing in the injection graph.
When a provider with a narrower scope (like transient or request) is injected into a wider scope provider (like singleton), NestJS creates separate instances per injection. But if a singleton injects a request-scoped provider, the request-scoped provider behaves like a singleton, which can cause bugs. To avoid this, request-scoped providers should not be injected into singletons directly.
Result
You understand how scope boundaries affect instance lifetimes and sharing in complex apps.
Knowing scope interactions prevents subtle bugs where scoped providers behave unexpectedly due to injection hierarchy.
7
ExpertPerformance and Memory Considerations of Scopes
🤔Before reading on: do you think using request or transient scopes always improves safety without cost? Commit to your answer.
Concept: Discusses the tradeoffs of using different scopes on app performance and memory usage.
Request and transient scopes create many instances, increasing memory use and garbage collection. Overusing them can slow down your app. Singleton scope is efficient but risks shared mutable state bugs. Experts balance scopes by using singleton for stateless services and request/transient only when isolation is essential.
Result
You can make informed decisions balancing safety and performance in provider scope choices.
Understanding the cost of scopes helps design scalable, maintainable NestJS applications.
Under the Hood
NestJS uses a dependency injection container that manages provider instances. When a provider is requested, the container checks its scope. For default scope, it returns the same instance stored in a global map. For request scope, it creates a new instance per HTTP request context, tracked internally. For transient scope, it creates a new instance every time the provider is injected, never caching it. This is implemented using internal metadata and context-aware factories.
Why designed this way?
The design balances efficiency and flexibility. Singleton scope minimizes resource use by reusing instances. Request scope isolates data per user interaction, essential for web apps. Transient scope offers maximum isolation for cases needing fresh instances every time. Alternatives like always singleton or always new instance were rejected because they either waste resources or cause data leaks.
┌─────────────────────────────┐
│ NestJS Dependency Injector  │
├─────────────┬───────────────┤
│ Provider    │ Scope Check   │
├─────────────┼───────────────┤
│ Default     │ Return cached │
│ (Singleton) │ instance      │
├─────────────┼───────────────┤
│ Request     │ Create new    │
│             │ instance per  │
│             │ HTTP request  │
├─────────────┼───────────────┤
│ Transient   │ Create new    │
│             │ instance every│
│             │ injection     │
└─────────────┴───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does request scope create one instance per user session or per HTTP request? Commit to your answer.
Common Belief:Request scope creates one instance per user session, so data is shared across multiple requests from the same user.
Tap to reveal reality
Reality:Request scope creates a new instance for every HTTP request, not per user session. Each request is isolated.
Why it matters:Believing request scope is per session can cause developers to store user session data in providers incorrectly, leading to bugs and data leaks.
Quick: Does transient scope reuse instances if injected multiple times in the same class? Commit to your answer.
Common Belief:Transient scope reuses the same instance within the same request or class injection.
Tap to reveal reality
Reality:Transient scope creates a new instance every time it is injected, even multiple times in the same request or class.
Why it matters:Misunderstanding this leads to unexpected behavior where state is not shared as expected, causing bugs in logic relying on shared instances.
Quick: If a singleton provider injects a request-scoped provider, does the request-scoped provider behave as request-scoped? Commit to your answer.
Common Belief:The request-scoped provider keeps its request scope behavior regardless of where it is injected.
Tap to reveal reality
Reality:When injected into a singleton, the request-scoped provider behaves like a singleton, losing request isolation.
Why it matters:This can cause serious bugs where request-specific data leaks across requests, breaking security and correctness.
Quick: Does using request or transient scope always improve app safety without downsides? Commit to your answer.
Common Belief:Using request or transient scope is always better because it isolates data and prevents bugs.
Tap to reveal reality
Reality:These scopes increase memory use and reduce performance due to many instances being created and destroyed.
Why it matters:Overusing these scopes can degrade app performance and scalability, so they must be used judiciously.
Expert Zone
1
Request-scoped providers require an active context; using them outside HTTP requests (like in background jobs) can cause errors.
2
Transient providers can cause subtle bugs if injected multiple times unintentionally, leading to inconsistent state within the same operation.
3
Combining scopes in complex dependency trees requires careful design to avoid scope mismatches and unexpected singleton promotion.
When NOT to use
Avoid request or transient scopes for heavy or long-lived services to prevent memory bloat. Use singleton scope for stateless or shared services. For per-user session data, use external session stores or context management instead of request scope.
Production Patterns
In production, singleton scope is the default for most services. Request scope is used for services handling user-specific data like authentication or request logging. Transient scope is rare but useful for lightweight helpers or factories that must be fresh every time. Experts also use custom context providers to manage complex lifecycles.
Connections
Dependency Injection
Provider scope is a feature built on top of dependency injection principles.
Understanding how dependency injection manages object lifecycles clarifies why scopes control instance creation and sharing.
HTTP Request Lifecycle
Request scope aligns provider lifetimes with the HTTP request lifecycle.
Knowing the HTTP request flow helps understand why request scope isolates data per user interaction.
Object Pooling in Game Development
Both manage object lifetimes to optimize resource use and performance.
Recognizing that scopes and object pools solve similar problems in different fields deepens understanding of resource management.
Common Pitfalls
#1Injecting a request-scoped provider into a singleton service.
Wrong approach:@Injectable() export class SingletonService { constructor(private readonly reqService: RequestScopedService) {} }
Correct approach:@Injectable({ scope: Scope.REQUEST }) export class RequestScopedService {} @Injectable({ scope: Scope.REQUEST }) export class SingletonService { constructor(private readonly reqService: RequestScopedService) {} }
Root cause:Singleton services are created once and cannot receive new instances per request, so request-scoped providers lose their scope when injected into singletons.
#2Marking all providers as transient without need.
Wrong approach:@Injectable({ scope: Scope.TRANSIENT }) export class HeavyService {}
Correct approach:@Injectable() export class HeavyService {}
Root cause:Misunderstanding transient scope leads to excessive instance creation, increasing memory use and hurting performance.
#3Assuming request scope shares data across multiple requests from the same user.
Wrong approach:Storing user session data in a request-scoped provider expecting it to persist across requests.
Correct approach:Use external session storage or cookies to persist user data across requests; keep request-scoped providers stateless or per-request only.
Root cause:Confusing request scope with session scope causes incorrect assumptions about data persistence.
Key Takeaways
Provider scope in NestJS controls how often and when service instances are created and shared.
Default scope creates a singleton shared across the app, request scope creates a new instance per HTTP request, and transient scope creates a new instance every injection.
Choosing the right scope prevents bugs related to shared state and improves app performance and safety.
Scopes affect the dependency injection tree and must be used carefully to avoid unexpected instance sharing or memory issues.
Understanding provider scopes is essential for building scalable, maintainable, and secure NestJS applications.

Practice

(1/5)
1. In NestJS, what does the default provider scope mean for a service instance?
easy
A. A new instance is created for each HTTP request.
B. A single instance is shared across the entire application.
C. A new instance is created every time the service is injected.
D. The service instance is destroyed after each method call.

Solution

  1. Step 1: Understand default provider scope behavior

    By default, NestJS creates one instance of a provider and shares it across the whole app.
  2. Step 2: Compare with other scopes

    Request scope creates per HTTP request instances, transient creates per injection instances, so default is the shared singleton.
  3. Final Answer:

    A single instance is shared across the entire application. -> Option B
  4. Quick Check:

    Default scope = singleton instance [OK]
Hint: Default scope means one shared instance app-wide [OK]
Common Mistakes:
  • Confusing default with request scope
  • Thinking default creates new instances per injection
  • Assuming default destroys instance after use
2. Which of the following is the correct way to set a provider's scope to transient in NestJS?
easy
A. @Injectable({ scope: Scope.REQUEST })
B. @Injectable({ scope: 'transient' })
C. @Injectable({ scope: Scope.TRANSIENT })
D. @Injectable({ transient: true })

Solution

  1. Step 1: Recall correct syntax for setting scope

    In NestJS, the scope is set using the Scope enum inside @Injectable decorator like { scope: Scope.TRANSIENT }.
  2. Step 2: Eliminate incorrect options

    @Injectable({ scope: Scope.REQUEST }) uses REQUEST scope, @Injectable({ scope: 'transient' }) uses string instead of enum, @Injectable({ transient: true }) uses invalid property.
  3. Final Answer:

    @Injectable({ scope: Scope.TRANSIENT }) -> Option C
  4. Quick Check:

    Use Scope.TRANSIENT enum for transient scope [OK]
Hint: Use Scope.TRANSIENT enum inside @Injectable decorator [OK]
Common Mistakes:
  • Using string 'transient' instead of Scope enum
  • Confusing transient with request scope
  • Using invalid decorator properties
3. Consider this NestJS service with transient scope:
@Injectable({ scope: Scope.TRANSIENT })
export class MyService {
  id = Math.random();
}
If this service is injected twice in the same controller, what will be true about the id values?
medium
A. Both injections share the same id value.
B. The service will throw an error on multiple injections.
C. The id value is always zero.
D. Each injection has a different id value.

Solution

  1. Step 1: Understand transient scope behavior

    Transient scope creates a new instance every time the service is injected, so each injection gets a fresh object.
  2. Step 2: Apply to the id property

    Since id is set to a random number on instance creation, each injection will have a different id.
  3. Final Answer:

    Each injection has a different id value. -> Option D
  4. Quick Check:

    Transient scope = new instance per injection [OK]
Hint: Transient scope means new instance each injection [OK]
Common Mistakes:
  • Assuming transient shares instance like default
  • Thinking id is constant or zero
  • Expecting errors on multiple injections
4. You set a service to request scope but notice it behaves like a singleton. What is the most likely cause?
medium
A. The controller injecting the service is not handling HTTP requests.
B. The service was not decorated with @Injectable.
C. The service scope was set incorrectly using a string instead of Scope.REQUEST.
D. The service is injected only once in the entire app.

Solution

  1. Step 1: Understand request scope requirements

    Request scope creates a new instance per HTTP request, so it only works if the provider is injected in a context handling requests.
  2. Step 2: Analyze the controller context

    If the controller is not handling HTTP requests (e.g., injected in a service or singleton), the request scope behaves like singleton.
  3. Final Answer:

    The controller injecting the service is not handling HTTP requests. -> Option A
  4. Quick Check:

    Request scope needs HTTP request context [OK]
Hint: Request scope needs HTTP request context to work [OK]
Common Mistakes:
  • Forgetting request scope needs HTTP context
  • Confusing decorator absence with scope behavior
  • Assuming string scope works like enum
5. You have a service with request scope injected into a transient-scoped service, which is then injected into a controller. How many instances of the request-scoped service are created per HTTP request?
hard
A. One instance per HTTP request.
B. One instance per injection of the transient service.
C. One instance per controller instance.
D. Multiple instances per HTTP request depending on injections.

Solution

  1. Step 1: Understand request scope lifetime

    Request-scoped services create one instance per HTTP request regardless of how many times they are injected.
  2. Step 2: Analyze injection chain

    Even if injected into a transient service (new instance per injection), the request-scoped service instance remains the same within the same HTTP request.
  3. Final Answer:

    One instance per HTTP request. -> Option A
  4. Quick Check:

    Request scope = one instance per HTTP request [OK]
Hint: Request scope always one instance per HTTP request [OK]
Common Mistakes:
  • Thinking transient causes multiple request-scoped instances
  • Confusing transient and request scope lifetimes
  • Assuming controller scope affects request-scoped service count
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard