Skip to main content
Modules are the fundamental unit of organization in nestrs. Every nestrs application has at least one root module, and larger applications divide their code into feature modules — each encapsulating a related set of controllers, providers, and imports. The #[module] proc-macro generates the Module and ModuleGraph trait implementations that NestFactory uses to build the provider registry and Axum router.

The #[module] macro

Annotate a plain struct with #[module] to define a module. The macro accepts four optional fields:
Modules that re-export another module’s providers use re_exports:

Composing feature modules

1

Define a feature module

Create a module for a focused area of your application. List only the providers and controllers that belong to that feature.
2

Import the feature module from your root module

List the feature module in imports. Exported providers are absorbed into the importing module’s registry.
3

Bootstrap the application

Pass the root module to NestFactory::create. nestrs builds the full provider registry and Axum router automatically.

Dynamic modules

Static #[module] declarations cover most use cases. When you need to configure a module at runtime — loading options from environment variables, a remote secrets store, or feature flags — use a DynamicModule.

DynamicModule::from_module

Converts any static module into a DynamicModule at call time:

ConfigurableModuleBuilder

The ConfigurableModuleBuilder pattern mirrors NestJS’s forRoot / forRootAsync:

Lazy modules

DynamicModule::lazy::<M>() runs M::build() at most once per process and shares the singleton cells across all callers. This is useful for shared infrastructure modules (databases, caches) that multiple feature modules import independently.

Lifecycle sequence

When you call NestFactory::create and then listen or listen_graceful, nestrs drives the provider registry through the following sequence for all singleton providers:
1

eager_init_singletons()

Constructs every singleton in the registry synchronously. This is where circular provider dependency panics surface.
2

run_on_module_init().await

Calls on_module_init on every singleton. Use this hook for async setup (opening database pools, warming caches) that must complete before the server accepts traffic.
3

run_on_application_bootstrap().await

Calls on_application_bootstrap on every singleton. Use this for tasks that depend on all modules being fully initialized.
4

Server accepts requests

The Axum TCP listener opens and the application begins serving traffic.
5

Shutdown: run_on_application_shutdown().await

On SIGTERM or graceful shutdown, on_application_shutdown is called on every singleton.
6

Shutdown: run_on_module_destroy().await

on_module_destroy is called last. Use this to close connections and flush buffers.
Lifecycle hooks only run for singleton providers. Transient providers are constructed on demand and do not participate in the global hook sequence.

Circular module imports

If two modules import each other, nestrs detects the cycle and panics with:
Mark the back-edge import with forward_ref to break the cycle:
When possible, extract shared types into a third module and import that from both sides instead of introducing a circular reference.