Skip to main content
Controllers group related route handlers under a common URL prefix. In nestrs, a controller is a plain Rust struct annotated with #[controller]. Route handlers live in an impl block annotated with #[routes]. When the module builds, nestrs registers every handler in that impl block as an Axum route under the controller’s prefix.

Defining a controller

The prefix argument sets the base path for every route in the controller. Paths are joined with each handler’s path, so prefix = "/api" + #[get("/")] produces GET /api/.

Optional attributes

Use the #[version] attribute separately to apply a version to an entire controller struct:

Registering routes with #[routes]

The #[routes] macro annotates an impl block and takes a state argument that names the injected service type. nestrs uses this to build an Axum router with the correct State type:
state must be a type registered in the module’s providers list. nestrs resolves it from the ProviderRegistry and injects it as Axum State.

Direct service parameter injection

Handlers do not need to manually wrap services in State(service): State<Arc<AppService>>. If a handler parameter is an Arc<T> matching the state type, #[routes] automatically transforms it into an Axum State extractor during macro expansion. Explicit extractor syntax (State(service): State<Arc<AppService>>) remains fully supported for backward compatibility or when using custom state wrappers.

Multi-service controllers (tuple state)

Controllers that depend on multiple services do not require manual composite state structs. You can provide a tuple of provider types directly to state:
nestrs automatically stitches together the composite tuple state from the module’s ProviderRegistry via RouteStateFromRegistry. Handlers only extract the specific services they declare in their parameters.

HTTP method macros

nestrs provides a macro for every HTTP method: Each macro takes the route path as its argument. The path is appended to the controller’s prefix.

Route examples

Basic GET and POST

Path parameters

Use Axum’s Path extractor. Parameters are declared in the route path with a colon prefix (:id):

Query parameters

Use Axum’s Query extractor with a deserializable struct:

JSON request bodies

Use Axum’s Json extractor for unvalidated JSON, or ValidatedBody to run validator constraints automatically:

Response customization

Custom HTTP status code

Custom response headers

Redirects

Versioning a single route

You can version a single route within a controller using #[ver] while leaving other routes at the controller’s default version:

Registering the controller in a module

Controllers must be listed in the controllers field of their module:
A single controller can only be registered in one module. Shared logic belongs in a provider (service), not a controller.