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 ;
use Endpoint;
use ;
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 theexecute()
method will return. This type must deriveDeserialize
. - The
transform
argument specifies an optional function which will be passed the raw response body as a String and return a String with any necessary modifications. In the example above, we're assuming the API returns responses wrapped in some generic wrapper (TestWrapper
) and so ourstrip
function parses the response, pulls the value out of the wrapper, and returns it. - 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 anexecute()
method 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").execute(&client);
.
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.execute;
// Produces POST http://api.com/test/path/test {"kind": "test"}
let result = builder.name.kind.execute;
// Produces POST http://api.com/test/path/test {"kind": "test", "optional": "yes"}
let result = builder.name.kind.optional.execute;
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