nestrs-core is the foundational crate that every nestrs crate depends on. It provides the compile-time DI container, the runtime module graph, route and metadata registries, and all core traits that guards, pipes, interceptors, and filters implement. You rarely use nestrs-core directly—nestrs re-exports everything under nestrs::core and nestrs::prelude—but understanding the types here helps you extend the framework.
# Cargo.toml (usually not needed directly; nestrs re-exports everything)
[dependencies]
nestrs-core = "*"
ProviderRegistry
The DI container. Built by #[module]-generated code, it stores one entry per registered type and resolves instances by TypeId.
pub struct ProviderRegistry { /* private */ }
Registration methods
| Method | NestJS analogue |
|---|
register::<T>() | useClass (derives from Injectable) |
register_use_value::<T>(Arc<T>) | useValue |
register_use_factory::<T, F>(scope, factory) | useFactory |
register_use_class::<T>() | Same as register::<T>() |
override_provider::<T>(Arc<T>) | Testing override / mock |
Resolution
// Returns Arc<T> — panics if T is not registered
pub fn get<T: Send + Sync + 'static>(&self) -> Arc<T>
Lifecycle methods
The framework calls these in order inside listen / listen_graceful:
pub fn eager_init_singletons(&self)
pub async fn run_on_module_init(&self)
pub async fn run_on_application_bootstrap(&self)
pub async fn run_on_application_shutdown(&self)
pub async fn run_on_module_destroy(&self)
Introspection
pub fn registered_type_ids(&self) -> Vec<TypeId>
pub fn registered_type_names(&self) -> Vec<&'static str>
Cross-module absorption
// Absorb all providers from another registry
pub fn absorb(&mut self, other: ProviderRegistry)
// Absorb only exported provider types from another registry
pub fn absorb_exported(&mut self, other: ProviderRegistry, exported: &[TypeId])
ProviderScope
Controls the lifetime of a provider instance.
pub enum ProviderScope {
Singleton, // One instance per application container (default)
Transient, // New instance on every resolution
Request, // One instance per request (requires use_request_scope middleware)
}
Set scope via the macro:
#[injectable(scope = "singleton")] // default
#[injectable(scope = "transient")]
#[injectable(scope = "request")]
Or programmatically when using register_use_factory:
registry.register_use_factory::<MyService, _>(
ProviderScope::Transient,
|_registry| Arc::new(MyService::new()),
);
Injectable trait
Every provider type implements this trait (usually generated by #[injectable]).
#[async_trait]
pub trait Injectable: Send + Sync + 'static {
fn construct(registry: &ProviderRegistry) -> Arc<Self>;
fn scope() -> ProviderScope {
ProviderScope::Singleton
}
async fn on_module_init(&self) {}
async fn on_module_destroy(&self) {}
async fn on_application_bootstrap(&self) {}
async fn on_application_shutdown(&self) {}
}
construct is always synchronous. Perform async initialization in on_module_init, which runs before the server accepts traffic.
Module trait
Generated by #[module]. Called once by NestFactory::create to build the full provider and router graph.
pub trait Module {
fn build() -> (ProviderRegistry, Router);
fn exports() -> Vec<TypeId> { Vec::new() }
}
Controller trait
Generated by #[controller] + #[routes]. Registers Axum routes into the router for a module.
pub trait Controller {
fn register(router: Router, registry: &ProviderRegistry) -> Router;
}
ModuleRef
The NestJS ModuleRef analogue. Provides typed access to the root ProviderRegistry after the application graph is built. Obtain it from NestApplication::module_ref().
pub struct ModuleRef { /* private */ }
impl ModuleRef {
pub fn get<T: Send + Sync + 'static>(&self) -> Arc<T>
pub fn registry(&self) -> &ProviderRegistry
pub fn registry_arc(&self) -> &Arc<ProviderRegistry>
pub fn into_inner(self) -> Arc<ProviderRegistry>
}
use nestrs::prelude::*;
#[derive(Default)]
#[injectable]
struct AppState;
#[module(providers = [AppState])]
struct AppModule;
fn main() {
let app = NestFactory::create::<AppModule>();
let mref = app.module_ref();
let state: std::sync::Arc<AppState> = mref.get::<AppState>();
let _router = app.into_router();
}
DiscoveryService
The NestJS DiscoveryService analogue. Introspects registered providers and compiled HTTP routes.
pub struct DiscoveryService { /* private */ }
impl DiscoveryService {
pub fn new(module: ModuleRef) -> Self
pub fn module_ref(&self) -> ModuleRef
pub fn get_providers(&self) -> Vec<TypeId>
pub fn get_provider_type_names(&self) -> Vec<&'static str>
pub fn get_routes(&self) -> Vec<RouteInfo>
}
let mref = app.module_ref();
let discovery = DiscoveryService::new(mref);
for name in discovery.get_provider_type_names() {
tracing::info!("registered provider: {name}");
}
for route in discovery.get_routes() {
tracing::info!("{} {}", route.method, route.path);
}
Unlike NestJS, there is no reflection over arbitrary class metadata. You get TypeId keys, type name strings, and whatever is in the global RouteRegistry. There is no runtime annotation discovery for arbitrary field metadata.
RouteRegistry
A process-wide registry populated by impl_routes! / #[routes] expansion at startup. Used by nestrs-openapi to generate the OpenAPI spec.
pub struct RouteRegistry; // static methods only
impl RouteRegistry {
pub fn list() -> Vec<RouteInfo>
}
pub struct RouteInfo {
pub method: &'static str,
pub path: &'static str,
pub handler: &'static str,
pub openapi: Option<&'static OpenApiRouteSpec>,
}
A process-wide key-value store for handler string metadata set by #[set_metadata] and #[roles].
pub struct MetadataRegistry; // static methods only
impl MetadataRegistry {
pub fn set(handler: &'static str, key: &str, value: &str)
pub fn get(handler: &str, key: &str) -> Option<String>
}
use nestrs::core::MetadataRegistry;
MetadataRegistry::set(
"my_app::controllers::UserController::list",
"roles",
"admin",
);
let value = MetadataRegistry::get(
"my_app::controllers::UserController::list",
"roles",
);
Access the current handler key from a guard via HandlerKey in request extensions:
pub struct HandlerKey(pub &'static str);
// Inside CanActivate::can_activate:
if let Some(key) = parts.extensions.get::<HandlerKey>() {
let roles = MetadataRegistry::get(key.0, "roles");
}
ExecutionContext
The NestJS ArgumentsHost analogue for HTTP requests. Available via the HttpExecutionContext extractor when NestApplication::use_execution_context() is enabled.
pub struct ExecutionContext { /* private */ }
pub enum HostType {
Http,
Microservice,
WebSocket,
}
CanActivate trait
The guard contract. Implement this on any unit struct (must be Default) and apply with #[use_guards].
#[async_trait]
pub trait CanActivate: Default + Send + Sync + 'static {
async fn can_activate(&self, parts: &Parts) -> Result<(), GuardError>;
}
pub enum GuardError {
Unauthorized(String), // → 401
Forbidden(String), // → 403
}
The pipe contract. ValidationPipe is the built-in implementation.
#[async_trait]
pub trait PipeTransform: Default + Send + Sync + 'static {
async fn transform(&self, value: serde_json::Value) -> Result<serde_json::Value, GuardError>;
}
AuthStrategy trait
Pluggable authentication strategy for AuthStrategyGuard. Implement this to verify tokens, sessions, or API keys.
#[async_trait]
pub trait AuthStrategy: Send + Sync + 'static {
async fn authenticate(&self, parts: &Parts) -> Result<(), AuthError>;
}
pub enum AuthError {
Unauthorized(String),
Forbidden(String),
}
DynamicModule and DynamicModuleBuilder
Runtime-composed module units for conditional imports, feature flags, and configurable modules.
pub struct DynamicModule {
pub registry: ProviderRegistry,
pub router: Router,
pub exports: Vec<TypeId>,
}
impl DynamicModule {
pub fn from_module<M: Module>() -> Self
pub fn from_router(router: Router) -> Self
pub fn from_parts(registry: ProviderRegistry, router: Router, exports: Vec<TypeId>) -> Self
pub fn lazy<M: Module + 'static>() -> Self // initialized at most once per process
}
DynamicModuleBuilder allows provider overrides before controllers are registered (useful for configurable modules and test setups):
let module = DynamicModuleBuilder::<MyModule>::new()
.override_provider::<MyService>(Arc::new(MockService))
.build();
ConfigurableModuleBuilder is the NestJS forRoot / forRootAsync pattern:
// Synchronous options
let module = ConfigurableModuleBuilder::<MyOptions>::for_root::<MyModule>(options);
// Async options (e.g. await a vault client)
let module = ConfigurableModuleBuilder::<MyOptions>::for_root_async::<MyModule>(|| async {
load_options_from_vault().await
}).await;