Learn how to write a REST HTTP service with Axum.
Router::new()
to create it and then adding a route with the route
method by chaining it. Below is an example that returns a router that has one route at the base that will return “Hello World!” if we go to localhost:8000
.
impl IntoResponse
type from Axum, which allows us to return a tuple of things that can convert into a HTTP response.
json!()
macro, or adding a derive macro to a struct - you can find more about this here.
We can also attach things like a request body type which we need to define as a type or a struct that can be (de)serialized to/from JSON via serde
. Here’s an example that takes a JSON request with a message field and spits it back out to the client that made the request as well as an OK status code indicating it was successful:
serde::Deserialize
and serde::Serialize
through the use of derive macros (a form of powerful Rust metaprogramming which you can find more about here. This then converts the received JSON into the struct.
We can also utilise dynamic routing by using the Path type, allowing us to use things like record IDs or article slugs as a variable in our function, as shown below.
/:id
) and then visit localhost:8000/32
(for example - assuming the server is run at port 8000), it should return this:
.nest()
to nest the deeper-level router first, and then we can use .route()
to add our higher-level routes.
with_state
function. We can also use FromRef<T>
to generate a subset of our original app state so that we can avoid having to share all of our variables everywhere:
tower-http
, which offers utility for serving your own static files whether you’re running a SPA, statically-generated files from a framework like Next.js or simply just raw HTML, CSS and JavaScript.
If you’re using static-generated files, you can easily slip this into your router (assuming your static files are in a ‘dist’ folder at the root of your project):
Tower
, which is the underlying crate for tower-http
. We can build a fallback service that uses this:
CookieBuilder
struct from the cookie
crate, re-imported to axum-extra
(requires the ‘cookie’ flag enabled). Let’s see what building this might look like:
Cookie::new
!
Deleting a cookie is also pretty much the same as adding a cookie; to be able to pass the changes properly, the deletion of the cookie needs to be specified in the return otherwise it will not delete properly:
layer
method is a very versatile method that we can use to layer multiple things - a CORS layer (which will be discussed later), middleware as well as tracing. what if we want to implement a middleware that has state? Thankfully, Axum has an aptly named from_fn_with_state
method that we can use instead of from fn
, like so:
with_state
method if your middleware function requires state to access things like a database connection pool.
You can read more about writing middleware with Axum here.
CorsLayer
type from tower-http
to assist with us being able to quickly set up CORS quickly and efficiently so that we don’t have to do everything ourselves. Let’s see what that would look like:
CorsLayer
that accepts GET and POST HTTP request methods from any origin. If we try to put in a DELETE request anywhere, CORS will automatically deny the request as it fails to meet CORS. We can also add an origin URL by using a string literal then parsing and unwrapping it in the allow_origin
method. However, if you’re using an environmental variable to store it then you can also just parse it as a http::HeaderValue
type like so:
shuttle deploy
and then it’ll deploy your app to a live URL (assuming there’s no errors). No Docker containerisation required!
If you’re migrating to Shuttle, you need to wrap your entry point function with the Shuttle runtime macro and then add relevant code annotations, and it’ll work without needing to do anything else. You can read more about this here.