Skip to main content
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

MethodNestJS 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>,
}

MetadataRegistry

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
}

PipeTransform trait

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;