How to Set Up a Basic Axum Project: A Step-by-Step Guide

If you’ve been exploring Rust and want to build web applications, Axum is a great framework to get started with. It’s built on top of Tokio and Hyper, offering a high-performance, type-safe way to create web services. In this article, we’ll walk through setting up a basic Axum project from scratch and build a simple web server together.

This guide is designed for developers who want to dive in and start coding right away. By the end, you’ll have a solid foundation to explore Axum further and build more complex applications.


Step 1: Setting Up Your Project

Before we get our hands dirty with code, let’s make sure everything is ready. Open your terminal and run the following command to create a new Rust project:

cargo new axum_hello_world --bin
cd axum_hello_world

This creates a new directory named axum_hello_world and initializes it as a binary crate. Now, let’s add Axum as a dependency.

Open the Cargo.toml file and add Axum under the [dependencies] section:

[dependencies]
axum = "0.6"
tokio = { version = "1", features = ["full"] }

Step 2: Writing the First Route

Let’s write our first Axum endpoint. Open src/main.rs and replace the existing code with this:

use axum::{Router, routing::get};
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
    // Create a new router and add a simple route
    let app = Router::new().route("/", get(|| async { "Hello, World!" }));

    // Set the address for the server to listen on
    let addr: SocketAddr = "127.0.0.1:3000".parse().unwrap();

    // Start the server
    println!("Server running on http://{}", addr);
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

Breakdown:

  1. Imports: We import Router and get from Axum. The get function allows us to define a simple GET route. We also bring in SocketAddr from std::net to specify where the server should listen.
  2. #[tokio::main]: This macro sets up the asynchronous runtime, which is required for handling web requests. It turns our main function into an async function.
  3. Router Setup: We create a new Router and define a single route (/) that responds with "Hello, World!" when accessed via a GET request.
  4. Server Binding: We specify the server address (localhost on port 3000) and tell Axum to start the server.

Step 3: Running the Server

Now that the code is written, let’s run the server. In your terminal, execute:

cargo run

You should see something like this in the terminal:

Server running on http://127.0.0.1:3000

To test the server, open your browser or use curl to visit http://127.0.0.1:3000. You should see:

Hello, World!

What just happened?

We set up a basic web server using Axum. The server listens on port 3000 and serves a "Hello, World!" message when accessed via a GET request. This is the simplest possible setup for an Axum application.


Step 4: Handling Parameters

Now let’s introduce some dynamic behavior. Let’s modify our route to accept a name parameter in the URL and say hello to the person by name. Update your code like this:

use axum::{Router, routing::get, extract::Path};
use std::net::SocketAddr;

async fn hello(Path(name): Path<String>) -> String {
    format!("Hello, {}!", name)
}

#[tokio::main]
async fn main() {
    // Create a new router and add a route that accepts a name parameter
    let app = Router::new().route("/hello/:name", get(hello));

    // Set the address for the server to listen on
    let addr: SocketAddr = "127.0.0.1:3000".parse().unwrap();

    // Start the server
    println!("Server running on http://{}", addr);
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

What’s new here?

  1. Extracting Parameters: We use Path to extract the name parameter from the URL. In this case, the URL /hello/:name will capture whatever comes after /hello/ and pass it to the hello function.
  2. Dynamic Response: The hello function takes the name and returns a personalized greeting using format!.

Step 5: Testing the New Route

With the new code in place, run the server again using cargo run. Now, try visiting http://127.0.0.1:3000/hello/Alice in your browser (or use curl). You should see:

Hello, Alice!

Change the Alice part of the URL to any name you like, and the server will respond with a customized greeting.


Step 6: Challenge – Add Another Route!

Here’s a quick challenge for you: Add another route that accepts a number and returns the square of that number. You can use a route like /square/:num and extract the number from the URL. Think about how you’d process the number and send back the result.

Try it out and see how it works!

Hint:

You’ll need to use Path to extract the number and convert it to an integer using str::parse. Then, return the square of the number.


Recap and Conclusion

Congratulations! You’ve just built a basic web server with Axum. Here's a summary of what we covered:

  1. Setting Up the Project: We created a new Rust project and added Axum as a dependency.
  2. Writing Your First Route: We set up a simple route that responds with "Hello, World!".
  3. Handling Parameters: We created a dynamic route that greets users by their name.
  4. Challenge: We introduced a small challenge to get you thinking about handling other types of data, like numbers.

Axum is a powerful framework, and this simple example just scratches the surface. Now that you have the basics, you can dive deeper into Axum’s features like middleware, error handling, and more complex routing.

For more learning, check out the official Axum documentation and explore more advanced features. Keep experimenting and building – happy coding!