Skip to content

Rust · NestJS-style modules & decorators

The Rust framework for modular, scalable backends.

You write the business logic; NestRS carries authentication, tenant isolation, field masking and transactions — declared as decorators, verified at boot.
×4.2
throughput
241k req/s on one core · ×2.5 vs Fastify
×25
less memory
8 MB under load · NestJS ~200 MB
×23
faster cold start
~15 ms · NestJS ~350 ms
0.42
p99 latency, ms
×3 lower · no GC pauses
0
unguarded routes
an ungated, unmasked surface cannot ship

Per core against NestJS 11, byte-identical contract — reproducible from the repo.

01 — SECURITY

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.

See it live — the security checkpoint →
users/http/controller.rsa normal controller
#[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    }}
authz/ability.rsone place for policy
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
two tokens, one route
 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

Same service, byte-identical contract, one pinned core each — the regime that favours Node. Reproduce with just bench.

Throughput — 1 core, req/s×2.5 vs Fastify
NestRS~241k
Fastify~97k
Express~58k
Latency p99 — lower is better×3.0
NestRS0.42 ms
Fastify1.27 ms
Express2.11 ms
Memory under load×25
NestRS8 MB
Fastify198 MB
Express206 MB
Cold start×23
NestRS~15 ms
Fastify~360 ms
Express~330 ms

04 — GUARANTEES

Secure by composition

Authn, row-level filtering, response masking, transaction scope — turned on by importing a module, never by remembering to call them.

Boot, not 3am

The DI graph is checked at boot: a bad import fails startup with the missing wire named. No resolution error five minutes after deploy.

Declarative decorators

66 decorators expanding to plain Rust you can read with cargo expand — no reflection, no magic strings.

Types you don't fight

Rust types end to end — entity, DTO, handler, schema, OpenAPI doc. No any, no casts at the boundaries.

Native throughput

×2.5 NestJS-on-Fastify — its best case — with sub-millisecond p99 and no GC pauses.

One lean artifact

A static native binary for a distroless image — ~8 MB under load, milliseconds to boot, probes for the orchestrator.

05 — SCALABILITY

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.

PUBLICexposed to clients
App A :3000a public surface
App B :3001another public surface
shared featuresone model — every app builds on it
PRIVATEoff the request path
App Cbackground work
App Dmore background work

Who runs where is a composition choice in each app's module.rs, not a fork of entities and services.

Scale each workload on its own signal
App A :3000demand · high traffic
App B :3001demand · steady
App C queue · backlog
App D queue · light

Same binary, more replicas, per app — a hot public surface follows demand, background apps follow queue depth. No single "scale the monolith" knob.

Coming from NestJS, the vocabulary carries over. Coming from Rust, the drift-prone parts stop being yours to police.

nestrs new helloGet started