Router
Group procedures, build handlers, and export bindings.
A Router<TCtx> collects procedures. TCtx is the context supplied by the transport when it executes a procedure.
This page uses the experimental API and dependency revision from the quickstart.
Attach procedures
use rspc::{Procedure, Router};
fn router() -> Router<()> {
Router::new().procedure("version", {
Procedure::builder::<ApiError>()
.query(|_: (), _: ()| async { Ok(env!("CARGO_PKG_VERSION")) })
})
}ApiError is the error type from the quickstart. Each procedure has one resolver: a query, mutation, or subscription. Use queries for reads and mutations for operations with side effects.
Request context
Put shared resources and request-specific data in a context struct. For example:
#[derive(Clone)]
struct Ctx {
message: std::sync::Arc<String>,
}
fn router() -> Router<Ctx> {
Router::new().procedure("message", {
Procedure::builder::<ApiError>()
.query(|ctx: Ctx, _: ()| async move { Ok((*ctx.message).clone()) })
})
}Create this context in the integration callback. Construct the router without opening a database connection or starting the server. This makes router construction easy to test.
The Axum WebSocket adapter creates context for operations from the connection's request parts. Those parts describe the original handshake, not a new HTTP request. If permissions can change during a connection, recheck them when the procedure runs.
Merge and nest
Use merge to combine routers at the same level. Use nest to put a router under a path segment:
let users = Router::new().procedure("list", {
Procedure::builder::<ApiError>()
.query(|_: (), _: ()| async { Ok(Vec::<String>::new()) })
});
let router = router().nest("users", users);The nested procedure's transport path is users.list. With the tuple client, call client.query(["users.list", null]).
Unlike the old API, merge does not take a prefix. Use router.merge(other_router) or router.nest("users", users_router). All combined routers must accept the same outer context type.
Build
let (procedures, types) = router.build().expect("invalid router");build consumes the router. It returns:
Procedures<TCtx>, the executable handlers passed to a transport.Types, the metadata passed to a binding exporter.
Duplicate procedure paths cause build to return an error. Add a test that builds the complete router so duplicates are caught before startup.
Exporting the TypeScript bindings
Enable rspc's typescript feature, then pass the built metadata to its exporter:
rspc::Typescript::default()
.header("/* eslint-disable */")
.export_to("./bindings.ts", &types)
.expect("failed to export bindings");This is rspc's exporter, not the current standalone Specta exporter. It includes procedure metadata as well as payload types. With the default legacy feature, it also exports ProceduresLegacy for the tuple client.
Export during development or in a dedicated test or generation command. Generate before frontend type checking. Production startup does not need write access to a bindings file if generation happens earlier.
Last updated on
