WebSockets & SSE
Push live updates to clients with WebSockets for bidirectional chat and Server-Sent Events for one-way streams.
Busca en todas las páginas de la documentación
Push live updates to clients with WebSockets for bidirectional chat and Server-Sent Events for one-way streams.
Quick-reference recipe card - copy-paste ready.
use axum::{extract::ws::{Message, WebSocket, WebSocketUpgrade}, response::IntoResponse, routing::get};
async fn ws_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
ws.on_upgrade(handle_socket)
}
async fn handle_socket(mut socket: WebSocket) {
while let Some(Ok(msg)) = socket.recv().await {
if let Message::Text(text) = msg {
let _ = socket.send(Message::Text(format!("echo: {text}"))).await;
}
}
}When to reach for this: Clients need live data without polling - chat, dashboards, or progress streams.
use axum::{
extract::ws::{Message, WebSocket, WebSocketUpgrade},
response::sse::{Event, KeepAlive, Sse},
routing::get,
Router,
};
use futures_util::stream::{self, Stream};
use std::{convert::Infallible, time::Duration};
use tokio_stream::StreamExt as _;
async fn ws_upgrade(ws: WebSocketUpgrade) -> impl axum::response::IntoResponse {
ws.on_upgrade(handle_ws)
}
async fn handle_ws(mut socket: WebSocket) {
while let Some(Ok(msg)) = socket.recv().await {
if let Message::Text(t) = msg {
if socket.send(Message::Text(format!("echo: {t}"))).await.is_err() {
break;
}
}
}
}
async fn sse_handler() -> Sse<impl Stream<Item = Result<Event, Infallible>>> {
let stream = stream::iter(0..5).map(|i| {
Ok(Event::default().data(format!("tick {i}")))
});
Sse::new(stream).keep_alive(KeepAlive::new().interval(Duration::from_secs(15)))
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/ws", get(ws_upgrade))
.route("/events", get(sse_handler));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}What this demonstrates:
WebSocketUpgrade performs the HTTP upgrade handshake.Message::Text frames.Sse streams Event items with keep-alive pings.Upgrade: websocket; Axum handles the handshake.text/event-stream and unidirectional server-to-client messages.| Transport | Direction | Fit |
|---|---|---|
| WebSocket | Bidirectional | Chat, games, collaborative editing |
| SSE | Server to client | Live feeds, notifications, metrics |
| Long poll | Request/response | Legacy clients without SSE |
// Broadcast channel fan-out to many WebSocket clients
let (tx, _) = tokio::sync::broadcast::channel::<String>(100);handle_ws stalls all messages. Fix: tokio::spawn per connection task with channels.X-Accel-Buffering: no.| Alternative | Use When | Don't Use When |
|---|---|---|
| Polling | Simple, rare updates | Sub-second latency requirements |
| gRPC streaming | Service-to-service | Browser clients without gRPC-Web |
tokio-tungstenite standalone | Non-Axum servers | Already on Axum |
Handle Message::Binary for protobuf or compressed payloads.
Clients auto-reconnect; include id field for resume semantics.
Sticky sessions or shared pub/sub (Redis) across instances.
Browsers check Origin during handshake - validate in upgrade handler.
Check send errors and close socket when client disconnects.
Terminate at load balancer; Axum sees plain HTTP behind proxy.
Use tokio-tungstenite client against test server.
Axum Sse sets headers automatically.
Same router - REST for CRUD, /ws or /events for live updates.
Axum 0.8 includes extract::ws - no separate crate required.
tokio-tungstenite clientsStack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Revisado por Chris St. John·Última actualización: 16 jul 2026