Naturally, in client-server applications, a client and a server negotiate with each other via some API (application programming interface), which often takes form of RPC (remote procedure call) for better structuring and standardizing (due IDL (interface definition language) usage).
Rust ecosystem provides support for all modern widely-used and adopted RPC technologies, and even comes with its own unique ones.
Since REST is rather an architecture convention/style than a strict specification for RPC, and RESTful APIs are typically loosely based on HTTP methods directly, there is usually no need in special frameworks in Rust to implement a RESTful API server or to request the one. Just any HTTP server or HTTP client will do.
This approach, however, suffers from lacking API schema, and so, makes it hard to build a rich ecosystem around with ready-to-use tooling (or connect with existing ones). Fortunately, this is easily solved by using a concrete RPC specification on top of REST conventions, and following it strictly.
For more information about REST, read through the following articles:
OpenAPI (former Swagger) is a specification for a machine-readable IDL (interface definition language), allowing to describe, produce, consume and visualize RESTful web APIs. In a nutshell, OpenAPI is a kind of REST-based RPC.
The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to HTTP APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. When properly defined, a consumer can understand and interact with the remote service with a minimal amount of implementation logic.
An OpenAPI definition can then be used by documentation generation tools to display the API, code generation tools to generate servers and clients in various programming languages, testing tools, and many other use cases.
In Rust ecosystem, most OpenAPI crates follow the code-first approach (generating OpenAPI schema from source code). The most notable crates for this are utoipa
and okapi
.
For the opposite (generating source code from OpenAPI schema) Rust ecosystem lacks its own pure implementation, and the original OpenAPI tool openapi-generator
should be used (powered by the swagger
crate).
For more familiarity with OpenAPI and using it in Rust, read through the following articles:
- OpenAPI Initiative
- SwaggerHub Documentation: OpenAPI 3.0 Tutorial
- Official
utoipa
crate docs - Official
okapi
crate docs - Twilio Docs: Generate a Rust client for Twilio's API
- Fabian Odenthal: Auto-Generating & Validating OpenAPI Docs in Rust: A Streamlined Approach with Utoipa and Schemathesis
GraphQL is a flexible query language for APIs, allowing to request data partially and compose multiple nested requests as a single one, seasoned with a schema having an expressive type system (comparing to other API schemas) and very strong introspection capabilities out-of-the-box.
One of the strongest parts of GraphQL is its whole ecosystem built around the language, allowing to auto-generate code from schema (or schema from code), have documentation directly from introspection, play interactively with any APIs in playgrounds, easily mock them, and much, much more. Once you've built your GraphQL schema, you have everything else ready-to-go.
Another strong part of GraphQL is that its protocol is transport-agnostic, so the same schema and queries, used via HTTP, are easily reusable via WebSocket, allowing to stream data with almost zero effort atop.
For more familiarity with GraphQL, read through the following articles:
For implementing a GraphQL server in Rust, there are two major crates in its ecosystem: juniper
(provides more static guarantees) and async-graphql
(more feature-rich). Both manifest code-to-schema approach (writing Rust code and later generating a GraphQL schema from it), because Rust type system is far more expressive than the GraphQL one.
juniper-from-schema
crate, however, tries to take it in opposite direction, and to some degree successfully provides schema-to-code approach (generating Rust code using juniper
from a provided GraphQL schema).
For more familiarity with implementing GraphQL server in Rust, read through the following articles:
- Official
juniper
crate docs - Juniper Book
- Official
juniper-from-schema
crate docs - Official
async-graphql
crate docs - Async-graphql Book
For making request to existing GraphQL APIs, you don't necessarily need a special crate in Rust for trivial cases, just any HTTP client is capable to send a simple query/mutation request.
However, if more static guarantees is needed, then the graphql-client
crate may be used, providing the query-to-code approach (Rust code is generated from GraphQL files defining queries).
cynic
crate takes the opposite code-to-query approach of generating a GraphQL query out of Rust code and validating it statically against a provided GraphQL schema.
For more familiarity with making GraphQL requests in Rust, read through the following articles:
gRPC is a widely-adopted high performance RPC framework, having a strict schema, powered with pluggable support for load balancing, tracing, health checking and authentication, built on top of HTTP/2 (and so, having a mandatory encryption), and heavily using code-from-schema generation.
For more familiarity with gRPC, read through the following articles:
For implementing a gRPC server in Rust, there are two main production-ready crates in its ecosystem: tonic
(pure Rust implementation, based on tokio
) and grpcio
(wrapper around gRPC core implementation).
In gRPC ecosystem, usually, implementing a gRPC client doesn't differ much from implementing a server, since both are auto-generated from the same .proto
schema. So, for Rust, the same tonic
and grpcio
crates do the job when it comes to making gRPC requests.
For more familiarity with using gRPC in Rust, read through the following articles:
Estimated time: 1 day
Rework the task from the previous step in a "thick client" paradigm:
- Server represents a RESTful API with separate endpoints for each operation.
- CLI client parses commands by itself and makes accurate requests to the server RESTful API.
It should be possible to perform all the operations via cURL (or any other HTTP/API client) directly on the RESTful API server, without using the CLI client.
Additionally, implement generation of OpenAPI schema out of you server RESTful API code, and generate HTML documentation from the generated OpenAPI schema.
Avoid architecture over-engineering for this task, just use simple, straightforward and obvious solutions.
After completing everything above, you should be able to answer (and understand why) the following questions:
- What is API? What is RPC? How do they relate?
- What does "code-first" approach mean? What does "schema-first" approach mean? Which advantages and disadvantages do they have?
- What does REST paradigm mean? What are essentials of RESTful API? Which strengths does it have? What does it lack?
- What is OpenAPI? What is Swagger? How do they relate? Why are they beneficial for RESTful API?
- What is GraphQL? Which are strong sides of this technology? What problems does it bring in practice?
- What is gRPC? What are its strengths? Which are good use-cases for it, and which are not? Why?