Arc-wrapped structs, and the Node.js runtime for Axum and Tower. The architectural ideas carry over almost directly—modules, controllers, guards, interceptors, pipes, and filters all have first-class analogues—but the implementation language is Rust, so some patterns look different even when they serve the same purpose.
Concept mapping at a glance
Side-by-side code: modules and controllers
- NestJS
- nestrs
Side-by-side code: injectable providers
- NestJS
- nestrs
Side-by-side code: guards
- NestJS
- nestrs
Side-by-side code: interceptors
- NestJS
- nestrs
Side-by-side code: DTO validation
- NestJS
- nestrs
Side-by-side code: metadata and roles
- NestJS
- nestrs
Guards, interceptors, and filters — semantics
All three cross-cutting concerns work before or around the route handler, just like NestJS:- Guards (
CanActivate) answer “may this request proceed?” and run before the handler. ReturningErr(GuardError::Forbidden(...))yields a 403 JSON response. - Interceptors (
Interceptor) wrap the handler withnext.run(req). Use them for logging, timing, and header injection. - Exception filters (
ExceptionFilter) rewrite responses that carry anHttpException. Register globally withNestApplication::use_global_exception_filteror per-route with#[use_filters].
DTOs and validation
NestJS usesclass-validator and class-transformer for runtime validation. nestrs uses the #[dto] macro (which derives serde::Deserialize and validator::Validate) combined with ValidatedBody<T> or #[use_pipes(ValidationPipe)] on a handler.
Unknown JSON fields are rejected by default —
#[dto] emits #[serde(deny_unknown_fields)] automatically. Use #[dto(allow_unknown_fields)] to opt out when you intentionally accept forward-compatible clients.Dependency injection differences
NestJS uses TypeScript constructor injection detected at runtime viareflect-metadata. nestrs constructs providers at compile time via the Injectable::construct method generated by #[injectable]. Each injectable receives a &ProviderRegistry and returns an Arc<Self>, pulling its own dependencies with registry.get::<DepType>().
There is no async constructor. Perform async I/O in on_module_init (called by the framework before listen starts serving traffic) or use ConfigurableModuleBuilder::for_root_async.
Configuration (ConfigModule → Rust patterns)
1
2
Pass options into a configurable module
Use
ConfigurableModuleBuilder::for_root or for_root_async so injectables receive ModuleOptions<O, M> from the registry.3
Use a typed settings injectable per bounded context
There is no global
ConfigService token; create one #[injectable] settings struct per module if that suits your team’s style.Testing
Packages and dependencies
What is not yet in nestrs
What to read next
NestFactory API
Bootstrap methods including microservice transports
NestApplication API
Full reference for every builder method
Routing macros
Controller, routes, and HTTP method macros
DI macros
Module, injectable, and metadata macros