Request / Response
naia supports typed request/response pairs over reliable bidirectional channels. This is useful for one-shot operations where the caller needs a reply: fetching a leaderboard entry, submitting an item purchase, or loading level data.
Defining a request/response pair
A request type derives Message and implements the Request trait, which
associates it with its response type. The response type derives Message and
implements the Response marker trait:
#![allow(unused)]
fn main() {
use naia_bevy_shared::{Message, Request, Response};
/// The request struct — carries the query parameters.
#[derive(Message)]
pub struct FetchScore {
pub player_id: u32,
}
impl Request for FetchScore {
type Response = FetchScoreResponse;
}
/// The response struct — carries the answer.
#[derive(Message)]
pub struct FetchScoreResponse {
pub score: u32,
pub rank: u32,
}
impl Response for FetchScoreResponse {}
}
There are public derives for Message, Channel, and Replicate. Request
and Response are marker traits: implement them as shown so naia knows which
response type belongs to which request.
Register the request type in the Protocol builder using add_request:
#![allow(unused)]
fn main() {
Protocol::builder()
.add_request::<FetchScore>()
.build()
}
Note: You register the request type only — the response type is discovered automatically through the
Request::Responseassociated type.
Sending a request (client)
#![allow(unused)]
fn main() {
// Returns Ok(ResponseReceiveKey) on success, Err if the channel is full.
let response_key = client.send_request::<RequestChannel, _>(
&FetchScore { player_id: 42 },
)?;
// Store `response_key` — you need it to match the reply when it arrives.
global.pending_requests.insert(response_key, PendingKind::FetchScore);
}
send_request returns a ResponseReceiveKey you use to match the reply.
Requests travel over a bidirectional reliable channel — register the channel
with ChannelDirection::Bidirectional and ChannelMode::OrderedReliable.
Handling the request (server)
#![allow(unused)]
fn main() {
use bevy_ecs::message::MessageReader;
use naia_bevy_server::{events::RequestEvents, Server};
fn handle_requests(mut server: Server, mut request_reader: MessageReader<RequestEvents>) {
for events in request_reader.read() {
for (_user_key, response_send_key, request) in
events.read::<RequestChannel, FetchScore>()
{
let score = db.lookup_score(request.player_id);
server.send_response(
&response_send_key,
&FetchScoreResponse { score, rank: 1 },
);
}
}
}
}
The server receives a response_send_key alongside the request. Pass it back
to send_response to route the reply to the correct client.
Receiving the response (client)
#![allow(unused)]
fn main() {
use naia_bevy_client::Client;
fn receive_responses(mut client: Client<Main>, mut global: ResMut<Global>) {
let mut finished = Vec::new();
for (response_key, _pending) in &global.pending_requests {
if let Some(response) = client.receive_response(response_key) {
println!("Score: {}, Rank: {}", response.score, response.rank);
finished.push(response_key.clone());
}
}
for response_key in finished {
global.pending_requests.remove(&response_key);
}
}
}
Bidirectional requests
Either side can send requests. In the Bevy demo, both the server and client issue requests to each other:
#![allow(unused)]
fn main() {
// Server sending a request to a client:
let response_key = server.send_request::<RequestChannel, _>(&user_key, &request)?;
// Client handling a request from the server and sending a response:
for (response_send_key, request) in events.read::<RequestChannel, BasicRequest>() {
client.send_response(&response_send_key, &BasicResponse { /* … */ });
}
}
TTL and disconnect cleanup
Pending requests are automatically cancelled when the connection drops. Unmatched
ResponseReceiveKey values become invalid and will not fire any event after
disconnect.
Tip: Use request/response for infrequent operations (level transitions, purchases, leaderboard queries). For high-frequency state that changes every tick, use entity replication instead — it is far more bandwidth-efficient thanks to per-field delta compression.
Full working example
See demos/bevy/shared/src/messages/basic_request.rs for the type definitions
and demos/bevy/server/src/systems/events.rs + demos/bevy/client/src/systems/events.rs
for the complete send/receive pattern.