rustify
A Rust crate which provides an abstraction layer over HTTP REST API endpoints
Rustify is a small crate which provides a way to easily scaffold code which communicates with HTTP REST API endpoints. It covers simple cases such as basic GET requests as well as more advanced cases such as sending serialized data and deserializing the result. A derive macro is provided to keep code DRY.
Rustify provides both a trait for implementing API endpoints as well as clients
for executing requests against the defined endpoints. Currently, only a client
using reqwest::blocking
is provided.
Presently, rustify only supports JSON serialization and generally assumes the remote endpoint accepts and responds with JSON.
Installation
cargo add rustify
Architecture
This crate consists of two primary traits:
- The
Endpoint
trait which represents a remote HTTP REST API endpoint - The
Client
trait which is responsible for executing theEndpoint
This provides a loosely coupled interface that allows for multiple
implementations of the Client
trait which may use different HTTP backends. The
Client
trait in particular was kept intentionally easy to implement and is
only required to send an HTTP request consisting of a URL, method, and body and
then return the final URL, response code, and response body. The crate currently
only provides a blocking client based on the
reqwest crate.
The Endpoint
trait is what will be most implemented by end-users of this
crate. Since the implementation can be verbose and most functionality can be
defined with very little syntax, a macro is provided via rustify_derive
which
should be used for generating implementations of this trait.
Usage
The below example creates a Test
endpoint that, when executed, will send a GET
request to http://api.com/test/path
and expect an empty response:
use Endpoint;
use Endpoint;
use Serialize;
Advanced Usage
This examples demonstrates the complexity available using the full suite of options offered by the macro:
use Builder;
use ReqwestClient;
use ;
use Endpoint;
use ;
use Value;
use skip_serializing_none;
Breaking this down:
- The
path
argument supports basic substitution using curly braces. In this case the final url would behttp://api.com/test/path/test
. Since thename
field is only used to build the endpoint URL, we add the#[serde(skip)]
attribute to informserde
to not serialize this field when building the request. - The
method
argument specifies the type of the HTTP request. - The
result
argument specifies the type of response that theexec()
method will return. This type must deriveDeserialize
. - The
builder
argument tells the macro to add some useful functions for when the endpoint is using theBuilder
derive macro fromderive_builder
. In particular, it adds abuilder()
static method to the base struct and theexec()
methods to the generatedTestBuilder
struct which automatically callsbuild()
onTestBuilder
and then executes the result. This allows for concise calls like this:Test::builder().name("test").kind("test").exec(&client);
.
Endpoints contain two methods for executing requests; in this example the
execute_m()
variant is being used which allows passing an instance of an
object that implements MiddleWare
which can be used to mutate the request and
response object respectively. Here the an arbitrary request header containing a
fictitious API token is being injected and the response has a wrapper removed
before final parsing.
This example also demonstrates a common pattern of using skip_serializing_none
macro to force serde
to not serialize fields of type Option::None
. When
combined with the default
parameter offered by derive_builder
the result is
an endpoint which can have required and/or optional fields as needed and which
don't get serialized when not specified when building. For example:
// Errors, `kind` field is required
let result = builder.name.exec;
// Produces POST http://api.com/test/path/test {"kind": "test"}
let result = builder.name.kind.exec;
// Produces POST http://api.com/test/path/test {"kind": "test", "optional": "yes"}
let result = builder.name.kind.optional.exec;
Error Handling
All errors generated by this crate are wrapped in the ClientError
enum
provided by the crate.
Testing
See the the tests directory for tests. Run tests with
cargo test
.
Contributing
- Fork it (https://github.com/jmgilman/rustify/fork)
- Create your feature branch (git checkout -b feature/fooBar)
- Commit your changes (git commit -am 'Add some fooBar')
- Push to the branch (git push origin feature/fooBar)
- Create a new Pull Request