You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Is there an existing issue that is already proposing this?
I have searched the existing issues
Is your feature request related to a problem? Please describe it
NestJS ensures a topological instantiation order for providers based on their dependency graph (i.e., new MyProvider(...) respects dependencies). However, this ordering is not enforced for asynchronous lifecycle hooks, such as:
Ideally, the Lifecycle Hooks documentation will explicitly clarify the following points:
Module Initialization Order: Modules are initialized sequentially in topological order during the init phase and reverse-topological order during teardown.
Provider Initialization: Within each module, providers' onModuleInit hooks are invoked sequentially in topological order.
Provider Teardown: Within each module, providers' onModuleDestroy hooks are invoked sequentially in reverse-topological order.
This clarification will help developers better understand the precise execution flow of lifecycle hooks.
What is the motivation / use case for changing the behavior?
Comparison with Other Popular Dependency-Injection Framework
My suggestion aligns with Spring's default behavior: https://docs.spring.io/spring-framework/reference/core/beans/factory-nature.html "The order of startup and shutdown invocations can be important. If a "depends-on" relationship exists between any two objects, the dependent side starts after its dependency, and it stops before its dependency".
Use Case Example
Consider a module with two providers:
A Kafka client provider that establishes a connection in onModuleInit().
A Kafka publisher provider (built on top of the client) that creates a test topic in onModuleInit().
Issue
Since both providers belong to the same module, Nest does not guarantee a topological execution order for their lifecycle hooks. This leads to potential failures:
Initialization (onModuleInit): The publisher may attempt to publish before the client has established a connection.
Teardown (onModuleDestroy): The publisher may attempt to send messages after the client has disconnected.
Current Approach to Mitigate the Issue
Developers can implement custom lifecycle coordination layers, but this undermines NestJS’s built-in orchestration capabilities. A practical workaround is to place each provider with an asynchronous onModuleInit or onModuleDestroy hook into its own dedicated module - effectively creating one-provider modules.
However, this approach introduces significant downsides: excessive modularity and fragmentation, as logically related providers become scattered across multiple modules.
The text was updated successfully, but these errors were encountered:
As the name of the lifecycle hook suggests, it triggers on the module initialization meaning provider dependencies are not taken into account. We'd need to add a new hook if we wanted to introduce such a feature.
As the name of the lifecycle hook suggests, it triggers on the module initialization meaning provider dependencies are not taken into account. We'd need to add a new hook if we wanted to introduce such a feature.
Terminology-wise, the term "module init" does not inherently convey an initialization policy - whether concurrent or sequential. Just as "task scheduling" does not specify whether it refers to recurring or one-time execution.
The decision to apply a sequential policy for module initialization while using a concurrent policy for providers within a module is an implementation detail.
Regardless of the outcome of this feature request, I believe this implementation detail ("meaning provider dependencies are not taken into account") is worth highlighting in the documentation: https://docs.nestjs.com/fundamentals/lifecycle-events
Introducing additional hooks, such as onProviderInit, would not necessarily clarify execution order either, as the name alone would not indicate whether providers are initialized sequentially.
A flexible and intuitive approach could be to introduce an optional argument to app.init() and/or app.listen() (or extend an existing option) that allows users to define their desired execution policy. For example:
Is there an existing issue that is already proposing this?
Is your feature request related to a problem? Please describe it
NestJS ensures a topological instantiation order for providers based on their dependency graph (i.e.,
new MyProvider(...)
respects dependencies). However, this ordering is not enforced for asynchronous lifecycle hooks, such as:onModuleInit
(initialization)onModuleDestroy
(teardown)Currently, all providers within the same module are initialized and destroyed concurrently (
Promise.all
), disregarding their dependency relations:https://github.com/nestjs/nest/blob/master/packages/core/hooks/on-module-init.hook.ts#L50
This is surprising because modules are initialized sequentially in topological order, ensuring a safeguard against race conditions. The "distance" terminology likely follows BFS levels, akin to Khan's algorithm:
https://github.com/nestjs/nest/blob/master/packages/core/nest-application-context.ts#L405-L408
nest/integration/hooks/e2e/on-app-shutdown.spec.ts
Line 23 in 2d4b2c8
Describe the solution you'd like
Feature Request
Lifecycle hooks within a module should execute sequentially in a topological (for init) and reverse-topological (for teardown) order to ensure:
onModuleInit
resolved) before its ownonModuleInit
executes.onModuleDestroy
resolved) before its ownonModuleDestroy
executes.This would improve lifecycle orchestration and prevent potential race-conditions.
Teachability, documentation, adoption, migration strategy
Ideally, the Lifecycle Hooks documentation will explicitly clarify the following points:
onModuleInit
hooks are invoked sequentially in topological order.onModuleDestroy
hooks are invoked sequentially in reverse-topological order.This clarification will help developers better understand the precise execution flow of lifecycle hooks.
What is the motivation / use case for changing the behavior?
Comparison with Other Popular Dependency-Injection Framework
My suggestion aligns with Spring's default behavior:
https://docs.spring.io/spring-framework/reference/core/beans/factory-nature.html
"The order of startup and shutdown invocations can be important. If a "depends-on" relationship exists between any two objects, the dependent side starts after its dependency, and it stops before its dependency".
Use Case Example
Consider a module with two providers:
onModuleInit()
.onModuleInit()
.Issue
Since both providers belong to the same module, Nest does not guarantee a topological execution order for their lifecycle hooks. This leads to potential failures:
onModuleInit
): The publisher may attempt to publish before the client has established a connection.onModuleDestroy
): The publisher may attempt to send messages after the client has disconnected.Current Approach to Mitigate the Issue
Developers can implement custom lifecycle coordination layers, but this undermines NestJS’s built-in orchestration capabilities. A practical workaround is to place each provider with an asynchronous
onModuleInit
oronModuleDestroy
hook into its own dedicated module - effectively creating one-provider modules.However, this approach introduces significant downsides: excessive modularity and fragmentation, as logically related providers become scattered across multiple modules.
The text was updated successfully, but these errors were encountered: