Skip to content

v0.4.0 - A long time in the making

Latest
Compare
Choose a tag to compare
@oscartbeaumont oscartbeaumont released this 28 Jan 09:39
· 2 commits to main since this release

Today marks the well anticipated release of rspc 0.4.0. This release has been over 2 years in the making and opens up a whole new world of possibilies.

Note: Currently none of the documentation has been upgraded to show the new syntax (#339). If you've got some time to spare a PR would be appreciated!

Changes

New Procedure Syntax

The new syntax looks like the following:

pub fn mount() -> Router<Ctx> {
    Router::new()
        .procedure("login", {
            <BaseProcedure>::builder()
                .query(|ctx, input: LoginRequest| ...)
        })
        .procedure("me", {
            <BaseProcedure>::builder()
                .with(auth())
                .query(|ctx, _: ()| ...)
        })
}

We have moved middleware from being applied to the router to being applied to the procedures themselves. This makes for much better composition and opens up many new usecases for middleware.

With this new syntax you are also responsible for defining BaseProcedure and Error. This may seem like a lot of boilerplate but it makes rspc way more flexible, which makes it scale much better as your codebase gets bigger. It can be defined as:

`BaseProcedure` + `Error` definition
use thiserror::Error;
use serde::Serialize;
use specta::Type;

#[derive(Debug, Error, Serialize, Type)]
#[serde(tag = "type")]
pub enum Error {}

impl rspc::Error for Error {
    fn into_procedure_error(self) -> rspc::ProcedureError {
        rspc::ResolverError::new(self, None::<std::io::Error>).into()
    }
}

pub struct BaseProcedure<TErr = Error>(PhantomData<TErr>);
impl<TErr> BaseProcedure<TErr> {
    pub fn builder<TInput, TResult>(
    ) -> rspc::ProcedureBuilder<TErr, Ctx, Ctx, TInput, TInput, TResult, TResult>
    where
        TErr: rspc::Error,
        TInput: rspc::ResolverInput,
        TResult: rspc::ResolverOutput<TErr>,
    {
        rspc::Procedure::builder() // You add default middleware here
    }
}

This change in syntax is very similar to what tRPC did for tRPC v9 to tRPC v10. A huge thanks to @alexdotjs for his influential work on tRPC!

New middleware syntax

The syntax needs some major documentation but we have also overhauled middleware to make them more flexible and give them access to switch the input and result type, along with accessing the input and result types in a typesafe manor.

We are also going to be releasing a suite of official middleware in the near future. A lot of them are in active development within the rspc repository.

If your interesting the new syntax is:

pub fn my_middleware<TError, TCtx, TInput, TResult>() -> Middleware<TError, TCtx, TInput, TResult>
where
    TError: Send + 'static,
    TCtx: Send + 'static,
    TInput: Send + 'static,
    TResult: Send + Sync + 'static,
{
    Middleware::new(
        move |ctx: TCtx, input: TInput, next| async move { next.exec(ctx, input).await },
    )
}

Glimpse into the future

Typesafe errors

This release supports typesafe error values on the backend. Your error types can be serialized and there types will be exported via Specta to your frontend.

We are currently lacking Typescript client support for this but it will be coming in a following up release.

Upgrading

Using rspc_legacy

You can change your imports from rspc to rspc_legacy to keep your legacy routers working on the new core. This will allow you to incrementally migrate procedures.

We have also renamed rspc-tauri to tauri-plugin-rspc so ensure you update your imports.

Checkout the legeacy example which shows how to do this.

Fully migration

Once your using rspc_legacy you can start migrating each router at a time to the new syntax.

Refer to the examples to see how the new syntax works.