Server
Error Handling

Error Handling

rspc procedures have to return the type Result<T, rspc::Error> where T can be any type which can be returned from a normal procedure.

The fact that Rust as a language currently requires the error type to be concrete makes error handling slightly annoying. All of the error handling done by rspc relys on the question mark operator (?) (opens in a new tab) in Rust to make a reasonable developer experience. The question mark operator will expand into something along the lines of return Err(From::from(err)) under the hood. This means for any type T if you implement From<T> for rspc::Error you will be able to rely on the question mark operator to convert it into an rspc::Error type.

An example using the rspc::Error type

src/main.rs
use rspc::{Error, Router};
 
let router = <Router>::new()
    .query("ok", |t| {
        t(|_, args: ()| {
            // Rust infers the return type is `Result<String, rspc::Error>`
            Ok("Hello World".into())
        })
    })
    .query("err", |t| {
        t(|_, args: ()| {
            // Rust is unable to infer the `Ok` variant of the result.
            // We use the `as` keyword to tell Rust the type of the result.
            // This situation is rare in real world code.
            Err(Error::new(
                ErrorCode::BadRequest,
                "This is a custom error!".into(),
            )) as Result<String, _ /* Rust can infer the error type */>
        })
    })
    .query("errWithCause", |t| {
        t(|_, args: ()| {
            some_function_returning_error().map_err(|err| {
                Error::with_cause(
                    ErrorCode::BadRequest,
                    "This is a custom error!".into(),
                    // This error type implements `std::error::Error`
                    err,
                )
            })
        })
    })
    .build();

Custom error type

src/main.rs
pub enum MyCustomError {
    ServerDidABad,
}
 
impl From<MyCustomError> for rspc::Error {
    fn from(_: MyCustomError) -> Self {
        rspc::Error::new(rspc::ErrorCode::InternalServerError, "Server did an oopsie".into())
    }
}
 
let router = <Router>::new()
    .query("returnCustomErrorUsingQuestionMark", |t| {
        t(|_, args: ()| Ok(Err(MyCustomError::ServerDidABad)?))
    })
    .query("customErrUsingInto", |t| {
        t(|_, _args: ()| {
            let res: Result<String, MyCustomError> = some_function();
            res.map_err(Into::into) // This is an alternative to using the question mark operator
        })
    })
    .build();