Skip to content

WebSockets

A WebSocket upgrade is an HTTP GET, so a gateway self-mounts on the existing HttpTransport — same port, same CORS, same TLS, no second server. Every frame rides one JSON envelope, { "event": "...", "data": ... }, and each #[subscribe_message("event")] method handles one event. List the gateway in #[module(providers = [...])] and you have real-time wired in.

There is no step-by-step tutorial for WebSockets yet. Read this section, then start from the working reference: the live app in Publish (a chat gateway plus org-scoped notifications).

nest-rs-ws is a thin layer over poem’s WebSocket support — the same transport nest-rs-http already uses. It hides the read/write split, the outbox writer task, the connection registry, and the dispatch plumbing behind one decorator.

In the Publish workspace, live comments and notifications run in live on port 3004.

Terminal window
cargo add nest-rs-ws

The smallest possible gateway: one event, one reply.

apps/live/src/chat/gateway.rs
use nest_rs_ws::{gateway, messages};
#[gateway(path = "/ws")]
#[derive(Default)]
pub struct PingGateway;
#[messages]
impl PingGateway {
#[subscribe_message("ping")]
async fn ping(&self) -> &'static str {
"pong"
}
}

#[gateway(path = "/ws")] mounts on the HTTP transport at /ws. #[messages] turns the impl block into a dispatcher: the read loop parses each frame’s envelope, matches the event, deserializes data, calls the handler, and serializes the return into a reply frame on the same event name.

Wire it in like any other provider:

apps/live/src/module.rs
use nest_rs_core::module;
use nest_rs_http::{HttpConfig, HttpModule};
use nest_rs_ws::WsModule;
use crate::chat::gateway::PingGateway;
#[module(
imports = [
HttpModule::for_root(HttpConfig { port: 3004, ..Default::default() }),
WsModule,
],
providers = [PingGateway],
)]
pub struct LiveModule;

HttpModule for the transport, WsModule for the connection registry (WsServer). The gateway itself is listed in providers — no .transport(...) call, no second port.

Terminal window
nestrs run dev live
Terminal window
2026-06-08T10:23:14Z INFO nest_rs::http: bound 1 gateway on 0.0.0.0:3004
GET /ws -> PingGateway (1 message, 0 lifecycle hooks)

Drive it from any WebSocket client. With websocat:

Terminal window
$ websocat ws://localhost:3004/ws
{"event":"ping","data":null}
{"event":"ping","data":"pong"}

The envelope you send is {event, data}; the server’s reply rides the same shape on the same event name. From here, every other concern is a matter of adding one decorator.

  • Messages — the envelope shape, the Result/Err return contract, payload validation, error frames.
  • RoomsWsClient::join / leave / broadcast / to, the per-connection handle.
  • Server-side pushWsServer<N> from outside a handler (queue processors, scheduled jobs, HTTP routes).
  • Namespaces — multiple gateways with isolated registries via WsServer<N> markers.
  • Guards — authenticate at the upgrade, gate per-message envelopes, the connection-vs-message scope split.
  • Security — wiring AuthGuard and AuthzGuard for WS the same way as HTTP and GraphQL.
  • nest-rs-ws — the macro and runtime: #[gateway], #[messages], #[subscribe_message], #[on_connect], #[on_disconnect], WsClient, WsServer, WsModule.
  • apps/live — end-to-end real-time example (ChatGateway, NotifyGateway) used throughout these pages.