lilydocs
HTTP APIWebSocketConsumer
Fundamentals3 min read

Controllers and namespaces

Create exact routes with explicit action and hook declarations, and learn which phase each controller metadata item applies to.

A controller groups actions belonging to a namespace. The namespace is selected when the connection starts; each message event calls an exact route within that namespace.

Canonical controller#

Use #[derive(WebSocketController)], #[namespace("hello")], fallible WebSocketControllerTrait::new, and #[websocket_controller] impl together. The complete first application demonstrates this pattern.

Controller impl snippet
#[websocket_controller]
impl HelloController {
    #[message("ping")]
    async fn ping(&self) -> Result<Ack<&'static str>, WebSocketActionError> {
        Ok(Ack::new("pong"))
    }
}

Add this snippet as an action in the existing macro impl, retaining the example's imports. The incoming event must be hello:ping and carry an ack_id for the ACK. Put helper methods in a separate, ordinary impl block. Actions under the macro are not discovered automatically from their names; declare #[message(...)] explicitly.

Route rules#

A namespace is at most 128 ASCII bytes. Namespace and action tokens contain letters, digits, -, _, and .. Although an action token can be up to 256 bytes, the combined namespace:event must also be at most 256 bytes. Matching is case-sensitive; hello:Greet is a different route. / does not mean a wildcard.

Duplicate namespaces or action collisions stop build. Each controller can define at most one connected and one disconnected hook.

Metadata placement#

Metadata Controller Action Lifecycle hook
Handshake / connection middleware Yes No No
Message middleware / guard Start of the message plan Additional local policy No
Frame codec Namespace default No No
Payload codec Message default Override No
Timeout Message default Override Independent hook-specific duration
AsyncAPI Documentation metadata Documentation metadata Not interpreted as a message operation

Registration examples appear on the middleware, guards, codecs, and AsyncAPI pages. Repeating the same concrete type in an inherited plan can cause a duplicate error.

Controller lifetime#

The constructor runs once per application. Finding a registration at link time does not mean there is one live controller instance for the whole process; separate WsApp instances create separate controller instances.

Retain shared Singleton services in controller fields. Resolve message-scoped services in the action through Service<T> or through the active exchange. For mutable fields, account for multiple connections accessing the same controller; sequential processing per connection does not mean every controller invocation runs in one global sequence.

Sources for this page v0.1.0

This guide is based on the reviewed framework commit.

QUICK START
Documentation
Source code