Skip to main content
nestrs DI is built on four proc macros that together replace NestJS’s decorator-based injection system. The #[module] macro declares the composition graph; #[injectable] marks types as providers resolved through Arc; #[set_metadata] and #[roles] attach string metadata to route handlers for guards and other pipeline components to read. All macros are re-exported from nestrs::prelude::*.

#[module]

Declares a struct as an application module. Generates the Module trait implementation that builds the ProviderRegistry and Axum Router by walking the import graph.
controllers
[Type, ...]
Controller structs annotated with #[controller] to register into the module’s router.
providers
[Type, ...]
Injectable structs annotated with #[injectable] to register into the module’s provider registry.
imports
[Type, ...] (optional)
Other module types or DynamicModule expressions whose exported providers are absorbed into this module’s registry.
exports
[Type, ...] (optional)
Provider types from this module’s registry to re-export so importing modules can resolve them.

Dynamic module imports

The imports list also accepts DynamicModule expressions, which lets you conditionally include modules:

#[injectable]

Marks a struct as a provider that the DI container can construct and resolve as Arc<Self>. Generates the Injectable trait implementation with a construct method that the ProviderRegistry calls.
The generated construct implementation calls registry.get::<OtherService>() for each field of type Arc<T> where T itself implements Injectable.

Scope variants

The default scope is Singleton (one instance per application container). Override it with the scope argument:
scope
&str (optional)
One of "singleton" (default), "transient", or "request". Request scope requires NestApplication::use_request_scope() to be called before listen.

Lifecycle hooks

Override any of these async methods on your type to hook into the application lifecycle:
construct is synchronous. Perform async I/O in on_module_init or use ConfigurableModuleBuilder::for_root_async to await initialization before the DI graph constructs singletons.

Full example with cross-module injection

#[set_metadata]

Attaches a key-value string pair to the route handler’s entry in MetadataRegistry. Evaluated at route registration time (compile-time macro expansion + runtime registration via impl_routes!).
key
&str
Metadata key. Used by guards and middleware to look up the value via MetadataRegistry::get(handler_key, key).
value
&str
Metadata value. All values are plain strings; serialize JSON yourself for structured data.
Read metadata in a guard using the HandlerKey extension inserted by the router:

#[roles]

Shorthand for #[set_metadata("roles", "...")]. Sets the roles metadata key to a comma-separated string of role names. Intended to be consumed by role-aware guards such as XRoleMetadataGuard.
roles
&str, ...
One or more role names as string literals. Multiple values are joined with commas in the metadata store.

Worked example: role-protected route

Send x-role: admin to receive 200. Sending any other role value yields 403.
Keep #[roles] on handlers even when enforcement lives in a different guard—it doubles as documentation of intent and is picked up by nestrs-openapi when infer_route_security_from_roles is enabled.