Rust and Axum: Building Simple Handlers for Different HTTP Methods (GET, POST, DELETE, etc.)

Rust is rapidly becoming a top choice for web development, especially with the powerful Axum framework. Axum, built on Tokio and Hyper, makes it easy to build fast and reliable web servers. In this guide, we’ll explore how to build simple HTTP handlers for GET, POST, and DELETE methods in Axum, using serde to handle JSON payloads.

By the end of this tutorial, you’ll have a basic web server running that handles GET, POST, and DELETE requests. Let’s get started step-by-step!


Step 1: Setting Up a New Rust Project

First, ensure you have Rust installed. If not, follow the installation instructions at rust-lang.org.

Create a new project:

cargo new axum_example
cd axum_example

Now, modify your Cargo.toml to include the necessary dependencies. Open Cargo.toml and add the following:

[dependencies]
axum = "0.6"
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
  • Axum: Web framework for routing and handling requests.
  • Tokio: Asynchronous runtime for Rust, needed for Axum to handle requests.
  • Serde: Library for serializing and deserializing data (we'll use it for handling JSON payloads).
  • Serde_json: This crate works alongside Serde for JSON support.

Step 2: Creating the First Handler (GET Method)

Let’s start by setting up a basic GET handler that returns "Hello, World!" when accessed.

Update src/main.rs as follows:

use axum::{
    routing::{get, post, delete},
    Json, Router,
};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct InputData {
    name: String,
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(root_handler))
        // .route("/submit", post(submit_handler))
        // .route("/delete", delete(delete_handler))
        ;

    // Run the server on localhost:3000
    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn root_handler() -> &'static str {
    "Hello, World!"
}

Explanation:

  • We’ve created a root_handler function for the GET method that simply returns "Hello, World!".
  • Axum Router: This is where we define the routes and associate them with handlers.
  • Tokio Runtime: This allows the Axum server to operate asynchronously, handling requests efficiently.

Run the server using:

cargo run

Now, if you visit http://localhost:3000/, you’ll see "Hello, World!" displayed.


Step 3: Adding a POST Handler

Next, let’s add a POST handler. We’ll create an endpoint that accepts JSON data and returns a message with the received data.

Update main.rs to include the POST handler:

async fn submit_handler(Json(payload): Json<InputData>) -> String {
    format!("Received data: {}", payload.name)
}

Now, our application will handle POST requests at /submit. We’ll receive a JSON object containing a name field.

To test this POST endpoint, use curl:

curl -X POST http://localhost:3000/submit -H "Content-Type: application/json" -d '{"name": "Rust Developer"}'

Response:

Received data: Rust Developer

Explanation:

  • Json extractor: The Json(payload) part extracts and deserializes the JSON body into the InputData struct.
  • Serde: We use serde for serializing and deserializing data. The InputData struct is annotated with #[derive(Serialize, Deserialize)] to automatically implement serialization and deserialization for this type.

Step 4: Adding a DELETE Handler

Now, let’s add a DELETE handler. The DELETE method is typically used for removing resources from the server.

We’ll create a simple handler that deletes an item based on a provided ID.

Update your main.rs to include a DELETE handler:

async fn delete_handler(Json(payload): Json<InputData>) -> String {
    format!("Deleted data for: {}", payload.name)
}

Explanation:

  • This handler accepts a JSON payload, deletes the corresponding item (simulated in this case), and returns a message confirming the deletion.

To test this DELETE endpoint, use curl:

curl -X DELETE http://localhost:3000/delete -H "Content-Type: application/json" -d '{"name": "Rust Developer"}'

Response:

Deleted data for: Rust Developer

Step 5: Challenge

Your challenge is to extend this application:

  1. Try adding a new endpoint for the PUT method. The PUT method is typically used for updating existing resources. It would follow a similar pattern to POST, but it’s used for updates rather than creating new data.
  2. Experiment with more complex data structures in your POST and DELETE payloads (e.g., nested objects or arrays).

Recap and Conclusion

In this tutorial, we’ve built a simple web server with Axum that handles GET, POST, and DELETE requests. We’ve also used serde for serializing and deserializing JSON data, which is a key feature when working with APIs.

To summarize:

  • GET handler: Returns a static response.
  • POST handler: Accepts and returns data via JSON.
  • DELETE handler: Simulates the deletion of a resource based on input data.

Axum is a great tool for building web APIs in Rust, and it’s especially useful for building efficient, asynchronous services. Now that you have a basic understanding of how to handle different HTTP methods, you can start exploring more advanced features such as middleware, authentication, and integrating with databases.

Keep exploring the Axum documentation for more advanced features and continue building your web API skills with Rust!

Happy coding!