Leptos Axum Socket

Realtime pub/sub communication for Leptos + Axum applications.
Usage
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
pub struct MyKey {
pub bla: String,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct MyMsg {
pub awesome_msg: String,
}
impl SocketMsg for MyMsg {
type Key = MyKey;
#[cfg(feature = "ssr")]
type AppState = AppState;
}
#[component]
pub fn MyComponent() -> impl IntoView {
let socket = expect_socket_context();
socket.subscribe(
MyKey {
bla: "bla".to_string(),
},
|msg: &MyMsg| {
leptos::logging::log!("message: {msg:#?}");
},
);
let on_click = move || {
socket.send(
MyKey {
bla: "bla".to_string(),
},
MyMsg {
awesome_msg: "awesome message".to_string(),
},
);
};
view! { "..." }
}
#[server]
pub async fn my_server_function() -> Result<(), ServerFnError> {
leptos_axum_socket::send(
&MyKey {
bla: "bla".to_string(),
},
&MyMsg {
awesome_msg: "Hello, world!".to_string(),
},
).await;
Ok(())
}
For this to work you have to prepare a little bit.
Define your app state in your lib.rs:
use leptos::prelude::*;
#[cfg(feature = "ssr")]
#[derive(Clone, axum::extract::FromRef)]
pub struct AppState {
pub socket: leptos_axum_socket::ServerSocket,
pub leptos_options: LeptosOptions,
}
Initialize your Axum app (probably in main.rs):
let state = AppState {
leptos_options: conf.leptos_options,
server_socket: ServerSocket::new(),
};
{
let mut server_socket = state.server_socket.lock().await;
server_socket.add_subscribe_filter(|key: MyKey, _ctx: &()| { key.bla == "bla" });
server_socket.add_send_mapper(|key: MyKey, msg: MyMsg, _ctx: &()| {
if key.bla == "bla" {
Some(MyMsg {
awesome_msg: msg.awesome_msg.replace("old", "new"),
})
} else {
None
}
});
}
let app = Router::new()
.leptos_routes(&state, routes, {
use basic::app::shell;
let leptos_options = state.leptos_options.clone();
move || shell(leptos_options.clone())
})
.socket_route(connect_to_websocket) .fallback(leptos_axum::file_and_error_handler::<AppState, _>(shell))
.with_state(state);
Implement the connect_to_websocket handler:
#[cfg(feature = "ssr")]
pub async fn connect_to_websocket(
ws: WebSocketUpgrade,
State(socket): State<ServerSocket>,
) -> Response {
let ctx = ();
upgrade_websocket( ws, socket, ctx)
}
And finally provide the context in your root Leptos component:
#[component]
pub fn App() -> impl IntoView {
provide_socket_context();
view! { "..." }
}