Using Axum Framework To Create Rest API (Part 1)

Abhinav Yadav
Intelliconnect Engineering
6 min readAug 4, 2022
Image from Nordic APIs

In this series of articles, we are going to build a REST API with the help of Rust programming language and Axum web Framework.

There will be part II of this article in which you will learn to authenticate API endpoint with JWT Token.

Technologies used in this article are:

  • Rust (as a programming language)
  • Axum (as a framework)
  • CockroachDB (as a database, You can also use Postgres)

What will you learn?

  • Extracting JSON from the request body
  • Sending JSON Response from the endpoint/handler
  • Handling or sending custom error responses from the handler/endpoint
  • Creating database connection pool and accessing it in handlers
  • Setting up CORS in the API
  • Separating code in different modules

You can find the code for this article on GitHub.

Setup the Project

Create a new binary rust project with cargo

// replace axum_api with what you want to name your projectcargo new axum_apicd axum_api

Add the below crates to the cargo.toml file

// cargo.toml[package]
name = "axum_api"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = {version = "0.5.13", features = ["headers"]}
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.68"
tokio = { version = "1.0", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tower-http = { version = "0.3.0", features = ["cors"] }
sqlx = { version = "0.6", features = [ "runtime-tokio-rustls", "postgres", "json" ] }

We are using sqlx for database operations, serde and serde_json for JSON request and response handling, tracing and tracing-subscriber for logging/debugging, tower-http for CORS middleware, and Axum as the base server framework.

We will also split our code into model (structs), controllers (endpoint handler functions) and error modules.

Writing code

First, we are going to write the main function which configures and runs the server, add the below code to src/main.rs

Above is a simple Axum server with tracing/logging. In line 14, you can replace axum_api=debug with you_app_name=debug.

Adding CORS

for adding CORS, modify the above code with the below code

On line 6 we are importing CorsLayer from tower-http and allowing all the origins on line 19 You can look at `tower-http` docs for customizing your CORS configuration, then adding the CORS layer to our server on line 23.

Database connectivity

For connecting the database, create an environment variable DATABASE_URL and set your database URL to it. Then make the below changes in your code.

On line 8 we are importing PgPoolOptions from sqlx for creating a pool of connections for the database. In line 12 we are getting the database URL from the env variable. On line 23 we are creating a connection pool for the database and finally, on line 32 we are passing the connection pool to the server as a state so that we can access it in all the handler functions. Don’t forget to import Extension from Axum at line 2.

File Structure

create the files and folder as below structure

axum_api
├── Cargo.toml
├── src
│ ├── controllers
│ │ └── auth.rs
│ ├── controllers.rs
│ ├── error.rs
│ ├── main.rs
│ ├── models
│ │ └── auth.rs
│ └── models.rs

└── target

We are going to create a register endpoint handler in src/controllers/auth.rs to register a user and save it’s email and password into the database, we will authenticate this user in part II of this article.

Models

in the src/models/auth.rs add the below code

this is the User struct we will use to extract JSON body from requests. sql::FromRow is derived, so we can deserialize User struct from sqlx query directly.

Add the below line to src/models.rs to include models folder in the models module, you can see more about module structure here.

pub mod auth;

and below line in main.rs somewhere at the top to include models module in our application

mod modules;

Register handler

Add the below code to src/controllers/auth.rs for register controller. The code won’t run yet, we will complete it later in the article.

In line 6 we are importing our error module, which we will write later in the article. At line 11 we are extracting the User email and password into the User model from req JSON body. On line 13 we are returning a JSON response if everything is ok, if an error occurred we return our custom AppError.

Inside the handler function, we are first checking if we get empty credentials ("”) then send an error. Then querying the database with this credential, if there is a user with this email then send an error “that a user already exists”. If there is no user with these credentials, then insert this user into the database. If it’s inserted into the database successfully, then send a JSON response with a success message, else send an error.

For database querying, you can check sqlx docs.

Add the below lines to respective files as mentioned on top

# src/controllers.rs
pub mod auth;
# src/main.rs
mod controllers;

Also add the route for registration in src/main.rs , the file should look like this

Error module

In Axum, we just have to implement IntoResponse a trait for our error struct to send it as a response from handlers.

Add the below code to error.rs

The above code contains the errors of part II also, but even if you leave it there, it will not cause any problem. You can customize the error messages you like.

Add the error module at the root of the project

// src/main.rs
mod error;

Running the server

To run the server, from the root of the project run the below command

// if you have set the DATABASE_URL
cargo run
// if not then
DATABASE_URL=postgresql://user:pass@host:port/database cargo run

the server will start on localhost:3000 or the port you have mentioned.

Debugging

if you are getting an error, you can compare your code with the code at GitHub. It contains code for part II also.

Also, ensure that there is a table named users with fields id, email, and password in the database.

Testing

you can test the API with Httpie with the below command

http POST localhost:3000/register email=abhinavy14@gmail.com password=pass@123

Conclusion

Stay updated for the Part II of this article, in which we are going to implement JWT-based authentication.

--

--