Rust · NestJS-style modules & decorators
The Rust framework for modular, scalable backends.
Per core against NestJS 11, byte-identical contract — reproducible from the repo.
01 — SECURITY
The part you cannot forget
Section titled “The part you cannot forget”A controller that looks like any controller — the security is in what you don’t write. Two declarations, and every route is authenticated, tenant-scoped and field-masked.
Two guards on the struct. Every route below requires a valid token and an ability — forgetting one is a boot error, not a 3am incident.
The handler is plain Rust. No security code inside — rows are filtered and fields masked on the way out, automatically.
Policy lives in one place. Which rows, which fields, for whom — one ability rule, applied to every transport.
#[controller(path = "/users")]#[use_guards(AuthnGuard, AuthzGuard)]● 1pub struct UsersController { #[inject] svc: Arc<UsersService>,} #[routes]impl UsersController { #[get("/")] async fn list(&self) -> Result<Vec<User>> { self.svc.list().await● 2 }}
ab.can(Action::Read, user::Entity) .when(|p| p.eq(user::OrgId, actor.org_id)) // your org only .fields([user::Id, user::Name]); // email never leaves● 3
❯ curl :3002/users -H "…Bearer $ADMIN"[{ "name": "Acme Admin", "email": "admin@acme.test", … }] ❯ curl :3002/users -H "…Bearer $MEMBER"[{ "name": "Acme Admin" }] # email masked, org-scoped ❯ curl :3002/users/<globex-id> -H "…$MEMBER"403 # cross-tenant read refused, by the framework
02 — PERFORMANCE
How fast, exactly
Section titled “How fast, exactly”Same service, byte-identical contract, one pinned core each — the regime that
favours Node. Reproduce with just bench.
03 — BATTERIES
Everything a backend needs
Section titled “Everything a backend needs”Not a router with plugins to assemble. Each concern is a module you import, shipped as a Cargo feature you can leave off — a headless worker compiles no HTTP stack.
31 capabilities, one dependency to install them. Packages →
04 — GUARANTEES
Why developers pick NestRS
Section titled “Why developers pick NestRS”Authn, row-level filtering, response masking, transaction scope — turned on by importing a module, never by remembering to call them.
The DI graph is checked at boot: a bad import fails startup with the missing wire named. No resolution error five minutes after deploy.
66 decorators expanding to plain Rust you can read with cargo expand — no reflection, no magic strings.
Rust types end to end — entity, DTO, handler, schema, OpenAPI doc. No any, no casts at the boundaries.
×2.5 NestJS-on-Fastify — its best case — with sub-millisecond p99 and no GC pauses.
A static native binary for a distroless image — ~8 MB under load, milliseconds to boot, probes for the orchestrator.
05 — SCALABILITY
One domain, several workloads
Section titled “One domain, several workloads”Entities, services and policy live once in a shared features crate; each app
under apps/* is a thin composition root importing only the transports it
serves. Microservice scaling without the distributed-systems tax.
Who runs where is a composition choice in each app's module.rs, not a fork of entities and services.
Same binary, more replicas, per app — a hot public surface follows demand, background apps follow queue depth. No single "scale the monolith" knob.
Start where you are
Section titled “Start where you are”Coming from NestJS, the vocabulary carries over. Coming from Rust, the drift-prone parts stop being yours to police.
❯ nestrs new helloGet started