Sharing API routes and types between frontend and backend

One of the important but tedious things in SPA development is to make sure that the API calls the frontend makes are actually implemented in the backend and that frontend and backend agree on the types of the functions offered by the API.

Running Rust both on server and client implies that we can trivially share the types that represent values exchanged between frontend and backend (e.g. by putting the types in a shared crate). This is nice and doesn’t involve any extra work.

Now, I want to go a step further than this: I want the compiler to statically check that the frontend only accesses URLs on the backend that are actually implemented by the backend, and also that frontend and backend agree on the types of values being passed between them. It is clearly possible to come up with a common API specification language and, from that, generate some code for frontend and backend (see, e.g. haskell-servant), so that the type checker will enforce this.

Question: does anyone here know of anything like this in the Rust ecosystem? Has anyone here done this in some way or another?

I’ve written only server_integration example with shared models between Seed and Actix but nothing more smart and robust.
We have also example with GraphQL - it generates Rust code from schemas - it should be used to verify contracts between client and server. Or you can investigate binary protocols like gRPC.
I want to investigate ways how to do something like https://discourse.elm-lang.org/t/announcing-lamdera-open-alpha/5669 in the far future.

I tried this with tide but it did not work. Tide using async-std and seed using tokio but that shared data cant implement for async trait when seed using tokio. So, i just defined same data in 2 crate. Also, for future, graphql will be fine.

Are you sure that Seed uses Tokio?

Well, i am not but compiler complained about executor. So i guessed “if it is not async-std, it should be tokio” :slight_smile:

Maybe the errors were different, i dont know. But when i removed async-std(sqlx was using it) trait from shared, it worked.

Thanks for pointing out the graphql libraries of Rust. I wasn’t aware of juniper and the graphql client. I’ll check those out. From their description, it sounds like they might actually do exactly what I am looking for.

I learned about one way to probably not do it: the graphql introspection result of juniper does not trivially convert to a graphql SDL schema, since the official graphql library on NodeJS fails to load the json schema from juniper’s introspection.
Fair enough, I guess that’s why there is a crate https://github.com/davidpdrsn/juniper-from-schema. Haven’t tried that yet. They are taking the approach of writing the graphql SDL schema by hand and generating the types and a trait for the API. It looks like implementing that trait means the graphql server is complete and adheres to the SDL schema.

1 Like