4 releases
Uses new Rust 2024
| new 0.2.0 | Apr 13, 2026 |
|---|---|
| 0.1.2 | Aug 21, 2025 |
| 0.1.1 | Aug 21, 2025 |
| 0.1.0 | Aug 21, 2025 |
#1137 in Asynchronous
Used in luarmor
39KB
803 lines
API Builder
This crate aims to make it easy to build API bindings in Rust. It is inspired by gitlab and how they build their API bindings.
For some examples, especially on the macros, view examples.
The ideology
The contents of the entire HTTP request, minus authentication (see handling authentication), is within a single endpoint struct. From there, users can supply their own client, response type, and other handlers via combinators. Therefore, your bindings will not lock users to a specific HTTP client and can customise the client and response to fit their needs.
The benefits
- It's more "rusty"
- Users can use both async and synchronous methods
- Easy testing via mock client implementations
- Customisable behaviour
- Custom response type
- Bring Your Own HTTP Client
- Supports "middleware" via combinators and custom clients
Handling authentication
Authentication is sensitive and if the bindings you make are supposed to be consumed directly by the user, you might not want them to supply their API token each time. While that approach is perfectly fine, it can be tedious and the behaviour can be abstracted to your own client which handles it all.
For example, you can make your own client skeleton which stores the API token and a user-supplied sub client.
From there, you implement RestClient and forward that implementation to the inner sub client.
However, you add custom logic to Client::rest/AsyncClient::rest_async which would add the corrosponding authentication headers.
Now, you can remove the API token from the endpoint struct and force users to use your client skeleton.
Alternatively, you can make your own combinator which takes in the endpoint and any tokens. The combinator approach might make it easier when these tokens are directly present in the body of the request, but you don't want to include them in the endpoint struct. However, this shifts the original problem to another place, and that's why I prefer the client route.
For a full example of the client approach, you can read luarmor-rs.
Additional advice
- Make sure to respect
Endpoint::ignore_errorswithin yourQuery/AsyncQueryimplementations - Use
typed_builderon your endpoint structs to make them easier to construct - Implement
APIClientErroron your custom client errors to get theFrom<E>(andTry) impl - Prefer
APIErroroverAPIErrorKindin return values.APIErrorKindimplementsInto<APIError>so it should be a drop-in replacement. The reason being is thatAPIErroris a boxed version ofAPIErrorKindso it's a lot nicer on the stack
Dependencies
~3–21MB
~263K SLoC