Today we dip a toe into the Axum web framework.
Axum is an unopinionated web application framework, written and maintained by the Tokio team. It’s very light and is built on top of the hyper HTTP library. Hyper reached a major milestone in November 2023, with it’s version 1.0 release. Axum was bumped to version 0.7 shortly after. The Tokio team wrote a great blog post explaining the major changes.
Alright, let’s get into some code…
// src/main.rs
// dependencies
use axum::{
response::{Html, IntoResponse},
routing::get,
Router,
};
use std::net::SocketAddr;
>use tokio::net::TcpListener;
// root endpoint handler, sends back a "Hello from Axum!" message in HTML
async fn hello_from_axum() -> impl IntoResponse {
Html(include_str!("assets/index.html"))
}
// main function, creates a Router type, attaches our handler to the "/" route
#[tokio::main]
async fn main() {
// create an app, has the Router type, has one index route
let app = Router::new().route("/", get(hello_from_axum));
// create an address
let addr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
// create a port
let port: u16 = 8000;
// create a socket to listen on
let socket = SocketAddr::new(addr, port);
// get a listener, use the socket we just created; the TcpListener::bind method could fail, so we use a match statement to handle that
// we panic if we can't get a listener, there's no point in going any further
let listener = match TcpListener::bind(&addr).await {
Ok(tcp_listener) => tcp_listener,
Err(err) => panic!("Could not get a TcpListener, {}", err),
};
// start up the server with our listener and the app; panics with an error message if the axum::serve function fails
axum::serve(listener, app)
.await
.expect("Could not start up the axum server");
}
Dependencies
First, we bring into scope several things from the axum crate itself:
- the
Html
type andIntoResponse
trait, both from the response module - the
get
method from the routing module, used to handle get requests - the
Router
type, used to compose handlers and services - the
SocketAddr
type from the standard library - the
TcpListener
type from the Tokio crate
Axum relies on the Tokio crate for asyncronous operations, so be sure Tokio is added as a dependency in the Cargo.toml configuration file.
Let’s look at the main function first, then we’ll talk about the single handler function as part of that.
Main Function
The general algorithm of our main function is:
- create an instance of our app; has one route at “/”, which accepts a get request and calls our
hello_from_axum
handler function- the
hello_from_axum
function takes no parameters, it simply returns some HTML created by reading in anindex.html
file using theinclude_str!
macro - the
impl IntoResponse
return type indicates that we leverage the fact that Axum knows how to return an Html message without us writing any extra code, the IntoResponse trait is already implemented for theHtml
type
- the
- create an address
- create a port
- use the address and port to create a socket
- create a listener, using the socket we just created, to listen for requests; there is some basic error handling here with a match statement, we panic if the attempt to get an address fails
- start up the server, using the
axum::serve
method, passing in the listener and app variables
Error handling is purposely left fairly basic. It’s generally not a good practice to panic and crash the program, but in this instance it makes sense. The application can’t run without an address to listen on or a listener to receive requests, so if these things fail there isn’t a lot of point in doing anything else.
That’s it!
If you typed everything in correctly, you can start up the basic server by typing:
cargo run
into your console. The server should start up and be listening on port 3000. If something happens error wise, you’ll see error messages returned at the console.
Head over to your web browser and type:
http://localhost:8000
You should be greeted with:
Pretty cool hey?
I know, not really. It’s pretty basic and not very interesting. Axum is terrific in its simplicity, but at the same time has much power and a great ecosystem. It really is garnering the mindshare of the Rust community and many projects appear to be standardizing around it. I bounced around between things for a long time, but have recently decided to just learn Axum properly.
There are a few warts on this simple server, chief of which is it doesn’t shut down very gracefully. I’ll show you how to do that in a future post.