Skip to main content
nestrs generate (aliased as nestrs g) creates individual source files or full multi-file resource scaffolds from built-in templates. Every generated file uses real nestrs macros and compiles out of the box. For resource generators, the CLI also automatically wires the new module into your parent main.rs, lib.rs, or mod.rs.

Syntax

nestrs g <kind> <name> [flags]
nestrs generate <kind> <name> [flags]
<name> is converted to snake_case for file names and field references and to PascalCase for struct names automatically.

Generator kinds and aliases

Full nameShort aliasDescription
resourceresFull CRUD scaffold: module, service, controller/resolver/gateway/transport, and two DTOs.
servicesInjectable service struct with a stub name() method.
controllercoController struct with a prefix attribute.
modulemoModule struct wiring a controller and service.
dtodtoDTO struct with #[IsString] and #[Length] validators.
guardguCanActivate implementation that returns Ok(()) by default.
pipepiPipeTransform<String> implementation with pass-through logic.
filterfiExceptionFilter implementation that delegates to HttpException::into_response.
interceptorinInterceptor implementation that calls next.run(req).
strategystBare strategy struct with a name() method.
resolverrBare resolver struct (GraphQL entry point placeholder).
gatewaygaBare gateway struct (WebSocket entry point placeholder).
microservicemsMicroservice struct with a transport() method.
transporttrBare transport struct.

Flags

FlagValuesDescription
--stylenest (default), rustControls file naming. nest uses name.kind.rs (e.g., users.controller.rs); rust uses name_kind.rs (e.g., users_controller.rs).
--pathany directoryBase directory for output. Defaults to src.
--dry-runPrint what would be created without writing any files.
--forceOverwrite files that already exist.
--quietSuppress all console output.
--transportrest, graphql, ws, grpc, microserviceResource generators only. Selects which transport layer to scaffold. Defaults to rest, or prompts interactively when stdin is a terminal.
--no-interactiveResource generators only. Skip the transport prompt and use rest when --transport is not provided.
Always run with --dry-run first when you are generating into an existing source tree. The CLI will print every file it would create — and show any parent module wiring — without touching the disk.

File naming: --style nest vs --style rust

Files are named <stem>.<kind>.rs, matching the Nest CLI convention:
src/users/
├── users.module.rs
├── users.controller.rs
├── users.service.rs
├── create_users.dto.rs
├── update_users.dto.rs
└── mod.rs
The mod.rs uses #[path = "..."] attributes to load the dotted filenames.

Automatic parent module wiring

After generating a resource, the CLI searches the --path directory for main.rs, lib.rs, or mod.rs in that order. If it finds one, it appends:
pub mod users;
use crate::users::UsersModule;
If no parent file is found, it prints the lines to add manually:
// ADD: pub mod users;
// ADD: use crate::users::UsersModule;
Automatic wiring only touches main.rs, lib.rs, or mod.rs. It will not modify arbitrary Rust files.

Resource generator: transport options

The resource generator is the most complete generator — it creates a module, service, two DTOs, a mod.rs, and a transport-specific entry file in one command.
# Interactive prompt (when stdin is a terminal)
nestrs g resource users

# Explicit transport
nestrs g resource users --transport rest --path src

# Dry run first
nestrs g resource users --transport graphql --path src --dry-run
Each transport produces a different entry file while sharing the same CRUD service and DTOs.

Generated files per transport

TransportEntry file kindFile (nest style)
restcontrollerusers.controller.rs
graphqlresolverusers.resolver.rs
wsgatewayusers.gateway.rs
grpcgrpcusers.grpc.rs
microservicetransportusers.transport.rs

Generator templates

The code below shows the exact template output for a generator named users (snake: users, pascal: Users). Real names are substituted at generation time.
use nestrs::prelude::*;

#[derive(Default)]
#[injectable]
pub struct UsersService;

impl UsersService {
    pub fn name(&self) -> &'static str {
        "users"
    }
}
use nestrs::prelude::*;

#[controller(prefix = "/users")]
pub struct UsersController;
use nestrs::prelude::*;

#[module(
    controllers = [UsersController],
    providers = [UsersService],
)]
pub struct UsersModule;
use nestrs::prelude::*;

#[dto]
pub struct UsersDto {
    #[IsString]
    #[Length(min = 1, max = 120)]
    pub name: String,
}
use nestrs::prelude::*;

/// Use in `impl_routes!`: `with (UsersGuard)` or `controller_guards(UsersGuard)`.
#[derive(Default)]
pub struct UsersGuard;

#[async_trait]
impl CanActivate for UsersGuard {
    async fn can_activate(
        &self,
        _parts: &axum::http::request::Parts,
    ) -> Result<(), GuardError> {
        Ok(())
    }
}
use nestrs::prelude::*;

/// Customize `Input` / `Output` / `Error` for your use case.
#[derive(Default)]
pub struct UsersPipe;

#[async_trait]
impl PipeTransform<String> for UsersPipe {
    type Output = String;
    type Error = HttpException;

    async fn transform(&self, value: String) -> Result<Self::Output, Self::Error> {
        Ok(value)
    }
}
use nestrs::prelude::*;

#[derive(Default)]
pub struct UsersFilter;

#[async_trait]
impl ExceptionFilter for UsersFilter {
    async fn catch(&self, ex: HttpException) -> axum::response::Response {
        ex.into_response()
    }
}
use nestrs::prelude::*;

#[derive(Default)]
pub struct UsersInterceptor;

#[async_trait]
impl Interceptor for UsersInterceptor {
    async fn intercept(
        &self,
        req: axum::extract::Request,
        next: axum::middleware::Next,
    ) -> axum::response::Response {
        next.run(req).await
    }
}
pub struct UsersStrategy;

impl UsersStrategy {
    pub fn name(&self) -> &'static str {
        "users"
    }
}
The standalone resolver generator produces a bare stub. For a full GraphQL CRUD resolver, use nestrs g resource users --transport graphql.
pub struct UsersResolver;
The standalone gateway generator produces a bare stub. For a full WebSocket gateway, use nestrs g resource users --transport ws.
pub struct UsersGateway;
use nestrs::prelude::*;

pub struct UsersMicroservice;

impl UsersMicroservice {
    pub fn transport(&self) -> &'static str {
        "message-based"
    }
}
The standalone transport generator produces a bare stub. For full message patterns, use nestrs g resource users --transport microservice or --transport grpc.
pub struct UsersTransport;
Shared across all transport kinds. Uses an in-memory HashMap store — swap it for a real database in production.
use nestrs::prelude::*;
use serde::Serialize;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

/// JSON / HTTP / message payload view of a `Users` row.
#[derive(Clone, Serialize)]
pub struct UsersResponse {
    pub id: u64,
    pub name: String,
}

/// In-memory CRUD store (swap for a real database in production).
#[injectable]
pub struct UsersService {
    store: Arc<RwLock<HashMap<u64, UsersResponse>>>,
    next_id: Arc<RwLock<u64>>,
}

impl Default for UsersService {
    fn default() -> Self {
        Self {
            store: Arc::new(RwLock::new(HashMap::new())),
            next_id: Arc::new(RwLock::new(1)),
        }
    }
}

impl UsersService {
    pub async fn list(&self) -> Vec<UsersResponse> {
        let g = self.store.read().await;
        g.values().cloned().collect()
    }

    pub async fn get(&self, id: u64) -> Option<UsersResponse> {
        self.store.read().await.get(&id).cloned()
    }

    pub async fn create(&self, name: String) -> UsersResponse {
        let mut idg = self.next_id.write().await;
        let id = *idg;
        *idg += 1;
        let row = UsersResponse { id, name };
        self.store.write().await.insert(id, row.clone());
        row
    }

    pub async fn update(&self, id: u64, name: String) -> Result<UsersResponse, HttpException> {
        let mut g = self.store.write().await;
        let entry = g
            .get_mut(&id)
            .ok_or_else(|| NotFoundException::new("not found"))?;
        entry.name = name;
        Ok(entry.clone())
    }

    pub async fn delete(&self, id: u64) -> Result<(), HttpException> {
        self.store
            .write()
            .await
            .remove(&id)
            .ok_or_else(|| NotFoundException::new("not found"))?;
        Ok(())
    }
}
The REST controller registers five routes and includes optional OpenAPI attributes. Add features = ["openapi"] to nestrs in Cargo.toml and chain .enable_openapi() on NestFactory to activate the /openapi.json endpoint.
use nestrs::prelude::*;
use std::sync::Arc;
use super::users_service::{UsersResponse, UsersService};

#[dto]
pub struct UsersIdParam {
    #[validate(range(min = 1))]
    pub id: u64,
}

#[controller(prefix = "/users")]
pub struct UsersController;

#[routes(state = UsersService)]
impl UsersController {
    #[openapi(summary = "List Users rows", tag = "users", responses = ((200, "OK")))]
    #[get("/")]
    #[serialize]
    async fn list(State(s): State<Arc<UsersService>>) -> Vec<UsersResponse> {
        s.list().await
    }

    #[openapi(
        summary = "Get Users by id",
        tag = "users",
        responses = ((200, "Found"), (404, "Not found"))
    )]
    #[get("/:id")]
    #[serialize]
    #[use_pipes(ValidationPipe)]
    async fn get_one(
        State(s): State<Arc<UsersService>>,
        #[param::param] p: UsersIdParam,
    ) -> Result<UsersResponse, HttpException> {
        s.get(p.id)
            .await
            .ok_or_else(|| NotFoundException::new("not found"))
    }

    #[openapi(summary = "Create Users", tag = "users", responses = ((200, "Created")))]
    #[post("/")]
    #[serialize]
    #[use_pipes(ValidationPipe)]
    async fn create(
        State(s): State<Arc<UsersService>>,
        #[param::body] body: super::create_users_dto::CreateUsersDto,
    ) -> UsersResponse {
        s.create(body.name).await
    }

    #[openapi(
        summary = "Update Users",
        tag = "users",
        responses = ((200, "Updated"), (404, "Not found"))
    )]
    #[put("/:id")]
    #[serialize]
    #[use_pipes(ValidationPipe)]
    async fn update(
        State(s): State<Arc<UsersService>>,
        #[param::param] p: UsersIdParam,
        #[param::body] body: super::update_users_dto::UpdateUsersDto,
    ) -> Result<UsersResponse, HttpException> {
        s.update(p.id, body.name).await
    }

    #[openapi(
        summary = "Delete Users",
        tag = "users",
        responses = ((200, "Deleted"), (404, "Not found"))
    )]
    #[delete("/:id")]
    #[serialize]
    #[use_pipes(ValidationPipe)]
    async fn delete(
        State(s): State<Arc<UsersService>>,
        #[param::param] p: UsersIdParam,
    ) -> Result<&'static str, HttpException> {
        s.delete(p.id).await?;
        Ok("deleted")
    }
}
Requires nestrs = { version = "...", features = ["graphql"] } and async-graphql = "=7.0.17" in Cargo.toml. Wire the schema in main.rs via app.enable_graphql(schema).
use async_graphql::{Context, Object, SimpleObject};
use nestrs::prelude::*;
use std::sync::Arc;
use super::users_service::{UsersResponse, UsersService};

#[derive(SimpleObject, Clone)]
pub struct UsersGqlRow {
    pub id: u64,
    pub name: String,
}

impl From<UsersResponse> for UsersGqlRow {
    fn from(r: UsersResponse) -> Self {
        Self { id: r.id, name: r.name }
    }
}

#[derive(Default)]
pub struct UsersGqlQuery;

#[Object]
impl UsersGqlQuery {
    async fn users_list(&self, ctx: &Context<'_>) -> async_graphql::Result<Vec<UsersGqlRow>> {
        let s = ctx.data::<Arc<UsersService>>()?;
        Ok(s.list().await.into_iter().map(Into::into).collect())
    }

    async fn users_get(&self, ctx: &Context<'_>, id: u64) -> async_graphql::Result<Option<UsersGqlRow>> {
        let s = ctx.data::<Arc<UsersService>>()?;
        Ok(s.get(id).await.map(Into::into))
    }
}

#[derive(Default)]
pub struct UsersGqlMutation;

#[Object]
impl UsersGqlMutation {
    async fn users_create(&self, ctx: &Context<'_>, name: String) -> async_graphql::Result<UsersGqlRow> {
        let s = ctx.data::<Arc<UsersService>>()?;
        Ok(s.create(name).await.into())
    }

    async fn users_update(
        &self,
        ctx: &Context<'_>,
        id: u64,
        name: String,
    ) -> async_graphql::Result<UsersGqlRow> {
        let s = ctx.data::<Arc<UsersService>>()?;
        s.update(id, name)
            .await
            .map_err(|e| async_graphql::Error::new(e.message.clone()))
            .map(Into::into)
    }

    async fn users_delete(&self, ctx: &Context<'_>, id: u64) -> async_graphql::Result<bool> {
        let s = ctx.data::<Arc<UsersService>>()?;
        s.delete(id)
            .await
            .map_err(|e| async_graphql::Error::new(e.message.clone()))?;
        Ok(true)
    }
}
Requires nestrs = { version = "...", features = ["ws"] }. Client frames use { "event": "...", "data": { ... } } JSON envelopes.
use nestrs::prelude::*;
use std::sync::Arc;
use super::users_service::UsersService;

#[ws_gateway(path = "/users/ws")]
#[injectable]
pub struct UsersGateway {
    service: Arc<UsersService>,
}

#[ws_routes]
impl UsersGateway {
    #[subscribe_message("users.list")]
    async fn list(&self, client: nestrs::ws::WsClient, _payload: serde_json::Value) {
        let rows = self.service.list().await;
        let _ = client.emit("users.list.ok", rows);
    }

    #[subscribe_message("users.get")]
    async fn get_one(&self, client: nestrs::ws::WsClient, payload: serde_json::Value) {
        let id = payload.get("id").and_then(|v| v.as_u64()).unwrap_or(0);
        let body = match self.service.get(id).await {
            Some(row) => serde_json::json!({ "ok": true, "item": row }),
            None => serde_json::json!({ "ok": false, "error": "not_found" }),
        };
        let _ = client.emit_json("users.get.ok", body);
    }

    #[subscribe_message("users.create")]
    async fn create(&self, client: nestrs::ws::WsClient, payload: serde_json::Value) {
        let name = payload
            .get("name")
            .and_then(|v| v.as_str())
            .unwrap_or("")
            .to_string();
        let row = self.service.create(name).await;
        let _ = client.emit("users.create.ok", row);
    }

    #[subscribe_message("users.update")]
    async fn update(&self, client: nestrs::ws::WsClient, payload: serde_json::Value) {
        let id = payload.get("id").and_then(|v| v.as_u64()).unwrap_or(0);
        let name = payload
            .get("name")
            .and_then(|v| v.as_str())
            .unwrap_or("")
            .to_string();
        let body = match self.service.update(id, name).await {
            Ok(row) => serde_json::json!({ "ok": true, "item": row }),
            Err(e) => serde_json::json!({ "ok": false, "error": e.message }),
        };
        let _ = client.emit_json("users.update.ok", body);
    }

    #[subscribe_message("users.delete")]
    async fn delete(&self, client: nestrs::ws::WsClient, payload: serde_json::Value) {
        let id = payload.get("id").and_then(|v| v.as_u64()).unwrap_or(0);
        let body = match self.service.delete(id).await {
            Ok(()) => serde_json::json!({ "ok": true }),
            Err(e) => serde_json::json!({ "ok": false, "error": e.message }),
        };
        let _ = client.emit_json("users.delete.ok", body);
    }
}
Requires nestrs features microservices and microservices-grpc. Bootstrap with NestFactory::create_microservice_grpc::<RootModule>(...).
use nestrs::prelude::*;
use std::sync::Arc;
use super::users_service::{UsersResponse, UsersService};

#[dto]
struct UsersGetReq {
    #[validate(range(min = 1))]
    id: u64,
}

#[dto]
struct UsersDeleteReq {
    #[validate(range(min = 1))]
    id: u64,
}

#[dto]
struct UsersUpdateMsgReq {
    #[validate(range(min = 1))]
    id: u64,
    #[IsString]
    #[Length(min = 1, max = 120)]
    name: String,
}

/// HTTP health routes + request/reply message patterns for `Users`.
#[controller(prefix = "/users")]
pub struct UsersHttpController;

#[routes(state = UsersService)]
impl UsersHttpController {
    #[get("/health")]
    async fn health() -> &'static str {
        "ok"
    }
}

#[injectable]
pub struct UsersTransport {
    service: Arc<UsersService>,
}

#[micro_routes]
impl UsersTransport {
    #[message_pattern("users.list")]
    async fn list(&self) -> Result<Vec<UsersResponse>, HttpException> {
        Ok(self.service.list().await)
    }

    #[message_pattern("users.get")]
    async fn get(&self, req: UsersGetReq) -> Result<UsersResponse, HttpException> {
        self.service
            .get(req.id)
            .await
            .ok_or_else(|| NotFoundException::new("not found"))
    }

    #[message_pattern("users.create")]
    async fn create(
        &self,
        body: super::create_users_dto::CreateUsersDto,
    ) -> Result<UsersResponse, HttpException> {
        Ok(self.service.create(body.name).await)
    }

    #[message_pattern("users.update")]
    async fn update(&self, req: UsersUpdateMsgReq) -> Result<UsersResponse, HttpException> {
        self.service.update(req.id, req.name).await
    }

    #[message_pattern("users.delete")]
    async fn delete(&self, req: UsersDeleteReq) -> Result<(), HttpException> {
        self.service.delete(req.id).await
    }
}
Requires the microservices feature. Bootstrap with NestFactory::create_microservice::<RootModule>(...). The generated code is identical to the gRPC transport except for the bootstrap method and the excluded microservices-grpc feature requirement.
use nestrs::prelude::*;
use std::sync::Arc;
use super::users_service::{UsersResponse, UsersService};

#[dto]
struct UsersGetReq {
    #[validate(range(min = 1))]
    id: u64,
}

#[dto]
struct UsersDeleteReq {
    #[validate(range(min = 1))]
    id: u64,
}

#[dto]
struct UsersUpdateMsgReq {
    #[validate(range(min = 1))]
    id: u64,
    #[IsString]
    #[Length(min = 1, max = 120)]
    name: String,
}

#[controller(prefix = "/users")]
pub struct UsersHttpController;

#[routes(state = UsersService)]
impl UsersHttpController {
    #[get("/health")]
    async fn health() -> &'static str {
        "ok"
    }
}

#[injectable]
pub struct UsersTransport {
    service: Arc<UsersService>,
}

#[micro_routes]
impl UsersTransport {
    #[message_pattern("users.list")]
    async fn list(&self) -> Result<Vec<UsersResponse>, HttpException> {
        Ok(self.service.list().await)
    }

    #[message_pattern("users.get")]
    async fn get(&self, req: UsersGetReq) -> Result<UsersResponse, HttpException> {
        self.service
            .get(req.id)
            .await
            .ok_or_else(|| NotFoundException::new("not found"))
    }

    #[message_pattern("users.create")]
    async fn create(
        &self,
        body: super::create_users_dto::CreateUsersDto,
    ) -> Result<UsersResponse, HttpException> {
        Ok(self.service.create(body.name).await)
    }

    #[message_pattern("users.update")]
    async fn update(&self, req: UsersUpdateMsgReq) -> Result<UsersResponse, HttpException> {
        self.service.update(req.id, req.name).await
    }

    #[message_pattern("users.delete")]
    async fn delete(&self, req: UsersDeleteReq) -> Result<(), HttpException> {
        self.service.delete(req.id).await
    }
}
Both DTOs are generated for every resource regardless of transport.
// create_users.dto.rs (nest style) / create_users_dto.rs (rust style)
use nestrs::prelude::*;

#[dto]
pub struct CreateUsersDto {
    #[IsString]
    #[Length(min = 1, max = 120)]
    pub name: String,
}
// update_users.dto.rs (nest style) / update_users_dto.rs (rust style)
use nestrs::prelude::*;

#[dto]
pub struct UpdateUsersDto {
    #[IsString]
    #[Length(min = 1, max = 120)]
    pub name: String,
}
The #[module] declaration varies by transport to wire the correct providers and controllers.
use nestrs::prelude::*;

#[module(
    controllers = [UsersController],
    providers = [UsersService],
)]
pub struct UsersModule;

Examples

nestrs g resource invoices --transport rest --path src
The generator will refuse to overwrite existing files unless you pass --force. Run with --dry-run first to preview output and avoid accidentally clobbering files in an existing module directory.