> ## Documentation Index
> Fetch the complete documentation index at: https://nestcrate.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Build microservices with nestrs transports

> Run nestrs microservice servers over TCP, NATS, Redis, Kafka, MQTT, RabbitMQ, or gRPC. Connect services with ClientsModule and the #[message_pattern] macros.

The `nestrs-microservices` crate adds message-passing primitives on top of nestrs's DI and module system. You annotate handler impl blocks with `#[micro_routes]` and individual methods with `#[message_pattern]` or `#[event_pattern]`, then boot a transport server with one of the `NestFactory::create_microservice_*` constructors. The same module, provider, and injectable primitives you use for HTTP work exactly the same way in microservice mode.

## Transport overview

| Transport | Factory method | Feature flag | Best for |
| - | - | - | - |
| TCP | `NestFactory::create_microservice` | `microservices` | Simple in-process or LAN request/reply |
| NATS | `NestFactory::create_microservice_nats` | `microservices-nats` | Low-ops, single-region messaging |
| Redis | `NestFactory::create_microservice_redis` | `microservices-redis` | Teams already running Redis |
| Kafka | *(via `KafkaMicroserviceServer`)* | `microservices-kafka` | High-throughput streaming, Kafka/Redpanda |
| MQTT | *(via `MqttMicroserviceServer`)* | `microservices-mqtt` | Mobile / IoT fan-out |
| RabbitMQ | `NestFactory::create_microservice_rabbitmq` | `microservices-rabbitmq` | AMQP work queues with per-request reply queues |
| gRPC | `NestFactory::create_microservice_grpc` | `microservices-grpc` | Cross-language RPC with strong contracts |

Add the features you need to `Cargo.toml`:

```toml theme={null}
[dependencies]
nestrs = { version = "1.6.0", features = ["microservices", "microservices-nats"] }
```

## Defining message handlers

Use `#[micro_routes]` on an `impl` block and `#[message_pattern]` / `#[event_pattern]` on individual methods. Message patterns expect a reply; event patterns are fire-and-forget:

```rust theme={null}
use nestrs::prelude::*;
use std::sync::Arc;

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

impl OrdersService {
    pub fn get_order(&self, id: u64) -> String {
        format!("order-{id}")
    }
}

pub struct OrdersController;

#[micro_routes(state = OrdersService)]
impl OrdersController {
    #[message_pattern("orders.find_one")]
    pub async fn find_one(
        State(svc): State<Arc<OrdersService>>,
        data: serde_json::Value,
    ) -> String {
        let id = data["id"].as_u64().unwrap_or(0);
        svc.get_order(id)
    }

    #[event_pattern("orders.created")]
    pub async fn on_created(data: serde_json::Value) {
        tracing::info!("order created: {:?}", data);
    }
}
```

## Booting a TCP microservice

```rust theme={null}
use nestrs::prelude::*;
use nestrs::microservices::TcpMicroserviceOptions;

#[module(
    controllers = [OrdersController],
    providers = [OrdersService],
)]
struct OrdersModule;

#[tokio::main]
async fn main() {
    NestFactory::create_microservice::<OrdersModule>(
        TcpMicroserviceOptions::bind("127.0.0.1:3001"),
    )
    .listen()
    .await;
}
```

## Booting a NATS microservice

```rust theme={null}
use nestrs::prelude::*;
use nestrs::microservices::NatsMicroserviceOptions;

#[tokio::main]
async fn main() {
    NestFactory::create_microservice_nats::<OrdersModule>(
        NatsMicroserviceOptions::new("nats://127.0.0.1:4222"),
    )
    .listen()
    .await;
}
```

## Hybrid HTTP + microservice mode

Use `also_listen_http` to run the HTTP router and the microservice transport in the same process:

```rust theme={null}
use nestrs::prelude::*;
use nestrs::microservices::NatsMicroserviceOptions;

#[tokio::main]
async fn main() {
    NestFactory::create_microservice_nats::<AppModule>(
        NatsMicroserviceOptions::new("nats://127.0.0.1:4222"),
    )
    .also_listen_http(3000)
    .configure_http(|app| {
        app.set_global_prefix("api")
            .use_request_tracing(RequestTracingOptions::builder())
            .enable_health_check("/health")
    })
    .listen()
    .await;
}
```

`configure_http` gives you the full `NestApplication` builder so you can apply all the usual HTTP middleware.

## Sending messages with ClientsModule

Register downstream services in a module using `ClientsModule::register`. This exports a `ClientsService` (and a default `ClientProxy` when exactly one client is registered) into the DI container:

```rust theme={null}
use nestrs::prelude::*;
use nestrs::microservices::{ClientConfig, ClientsModule, TcpMicroserviceOptions};

#[module(
    imports = [
        ClientsModule::register(&[
            ClientConfig {
                name: "ORDERS_SERVICE",
                transport: Transport::Tcp(TcpMicroserviceOptions::bind("127.0.0.1:3001")),
            },
        ]),
    ],
    controllers = [GatewayController],
    providers = [GatewayService],
)]
pub struct GatewayModule;
```

Inject `ClientsService` into a provider and use `send_json` for request/reply or `emit_json` for fire-and-forget:

```rust theme={null}
use nestrs::prelude::*;
use nestrs::microservices::ClientsService;
use std::sync::Arc;

#[injectable]
pub struct GatewayService {
    clients: Arc<ClientsService>,
}

impl GatewayService {
    pub async fn get_order(&self, id: u64) -> serde_json::Value {
        let client = self.clients.expect("ORDERS_SERVICE");
        client
            .send_json("orders.find_one", serde_json::json!({ "id": id }))
            .await
            .unwrap_or(serde_json::Value::Null)
    }

    pub async fn notify_created(&self, order: serde_json::Value) {
        let client = self.clients.expect("ORDERS_SERVICE");
        let _ = client.emit_json("orders.created", order).await;
    }
}
```

## gRPC transport

Enable the `microservices-grpc` feature and use the gRPC-specific factory and options:

```toml theme={null}
nestrs = { version = "1.6.0", features = ["microservices", "microservices-grpc"] }
```

```rust theme={null}
use nestrs::prelude::*;
use nestrs::microservices::GrpcMicroserviceOptions;

#[tokio::main]
async fn main() {
    NestFactory::create_microservice_grpc::<OrdersModule>(
        GrpcMicroserviceOptions::bind("0.0.0.0:50051"),
    )
    .listen()
    .await;
}
```

For clients, use `GrpcTransportOptions` and chain `.with_request_timeout` for long-running RPCs:

```rust theme={null}
use nestrs::microservices::{GrpcTransportOptions, ClientConfig, Transport};
use std::time::Duration;

ClientConfig {
    name: "ORDERS_SERVICE",
    transport: Transport::Grpc(
        GrpcTransportOptions::new("http://orders-svc:50051")
            .with_request_timeout(Duration::from_secs(30)),
    ),
}
```

<Note>
  The gRPC transport carries the same JSON wire format (`WireRequest` / `WireResponse`) inside protobuf bytes. Handler code is identical across transports — only the transport configuration changes.
</Note>

## Cross-cutting in micro handlers

On `#[micro_routes]` handlers, the execution order is:

```
request → interceptors (outer → inner)
        → guards
        → pipes
        → handler
```

Apply cross-cutting with the micro-specific attributes:

```rust theme={null}
#[micro_routes(state = OrdersService)]
#[use_micro_interceptors(LoggingInterceptor)]
#[use_micro_guards(AuthGuard)]
impl OrdersController {
    // ...
}
```

<Warning>
  There is no microservice exception-filter pipeline. Handlers return `Result<_, TransportError>`. Guards and pipes return `TransportError` for early rejection. This differs from the HTTP pipeline where `CanActivate` flows through `use_global_exception_filter`.
</Warning>

## Event bus (in-process)

For in-process async events without a transport, use `EventBus` directly:

```rust theme={null}
use nestrs::EventBus;

// Subscribe
EventBus::subscribe("user.created", |payload: serde_json::Value| async move {
    tracing::info!("user created: {:?}", payload);
});

// Emit
EventBus::emit("user.created", serde_json::json!({ "id": 42 })).await;
```

Handlers annotated with `#[on_event("...")]` inside an `#[event_routes]` impl block are auto-subscribed at app boot.

## Transport hardening (TCP)

The TCP transport applies fixed resource bounds so a misbehaving or hostile peer cannot exhaust server memory:

| Bound | Value | Behavior |
| - | - | - |
| Max frame size | 1 MiB | Larger frames are rejected before allocation; oversized JSON payloads get an error reply instead of being buffered. |
| Connection idle timeout | 30 s | Connections with no traffic are closed. |
| Client request timeout | 30 s | A `send` call to an unresponsive peer errors instead of hanging forever. |
| Max concurrent connections | 1024 | Enforced with a connection semaphore; connections past the cap are refused and the drop is logged (visible in `tracing`). |

These are protocol-level defaults, not configurable knobs — size large payloads differently (chunk, or move to Kafka/NATS for big messages) rather than trying to raise them.

## Broker failure semantics

What happens when a message cannot be processed is transport-specific:

* **RabbitMQ** — malformed (undeserializable) messages are `basic_nack`ed with **requeue disabled**: they are dropped, not redelivered. A handler **panic** is caught (`catch_unwind`); the error is published to the reply queue for RPC patterns and the message is nacked without requeue. Poison messages therefore never wedge the consumer's prefetch slots — a crashing handler cannot stall the whole work queue.
* **Kafka** — the listener keeps its position **in memory** (rskafka has no offset-commit API). Restarts follow `KafkaConsumerStart::Latest` (default, skip retained history) or `Earliest` (full replay). Delivery across restarts is at-most-once; make handlers idempotent.
* **NATS core** — messages are ephemeral; an offline subscriber misses them. Use JetStream for integration events you must not lose.
* **Redis / TCP** — at-least-once on retry; treat every handler as re-executable.

## Reliability guidance

* Use `emit_json` for integration events (`order.created`, `user.updated`) and include an `event_version` field for forward compatibility.
* Use `send_json` for request/reply patterns where a response contract is required.
* Assume at-least-once delivery — keep handlers idempotent and include correlation IDs in payload metadata.
* Add a dead-letter or retry strategy per transport adapter.
* For critical integration events, use the outbox pattern: write business state and an outbox row in one DB transaction, then publish asynchronously with retries.
