How Build Rest API with Rust and MySQL

Rust is a popular programming language that has been gaining traction as a reliable, performant choice for developing applications. One of the most powerful features of Rust is its ability to quickly and easily build Rest APIs. In this article, we'll look at how to use the actix-web framework to build a Rest API with Rust.

Actix-web

Actix-web is a powerful web framework for Rust that makes it easy to build high-performance web applications. As of now, actix-web is one of our go-to web frameworks for building web applications in Rust.

It has a range of features, such as support for HTTP2, websocket, a powerful routing system, and various integrations with popular web frameworks. It also integrates well with other Rust libraries, such as Diesel for database interactions.

Setting up the Environment

Installing Rust and MySQL

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Configuring MySQL

If you are using windows download the installer here.

On linux.

sudo apt update
sudo apt install mysql-server
sudo systemctl start mysql.service

On mac.

brew install mysql
brew services start mysql

In this article we will using this version of rust and mysql.

$ mysql --version
mysql  Ver 8.0.31 for macos13.0 on arm64 (Homebrew)

$ rustc --version
rustc 1.64.0 (a55dd71d5 2022-09-19)

Building the Rest API

Now let's setting up a Rust project.

cargo new rest-api-mysql

Then let's add dependencies we needed. But before that we need to install cargo package called cargo-edit, so the cargo it self doesn't have support to manage dependency via the commandline such as adding or removing dependency. With package cargo-edit will extend cargo cli to be able tomanage dependency with sub command add and rm.

cargo install caro-edit

Now we can add new dependency like this.

cargo add actix actix-web

With that all set we can write our first web server like this.

use actix_web::{web, App, HttpRequest, HttpServer, Responder};

async fn index(req: HttpRequest) -> impl Responder {
    let name = req.match_info().get("name").unwrap_or("World");
    format!("Hello {}!", &name)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/", web::get().to(index))
            .route("/{name}", web::get().to(index))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

Let's start to testing our server using curl.

curl http://localhost:8080
# It should give the response like this.
Hello World!

And next we can test the other endpoint by this curl.

curl http://localhost:8080/ahmadrosid
# It should give the response like this.
Hello ahmadrosid!

Connecting to the MySQL database

To connect with the database we will use another package called diesel. So Diesel is a Safe, Extensible ORM and Query Builder for Rust. Rist now this package is one of the most used when it come work with database. You can learn more about it here.

Install diesel cli, this package will be used for creating the database migration and generating the schema.

cargo install diesel_cli --no-default-features --features mysql

Now let's add the diesel package to our project.

cargo add diesel --features mysql
cargo add diesel --features r2d2

Now let's create our first databse migration. First let's create folder for storing our migration files.

mkdir migrations

Create new migration file.

diesel migration generate create_forum_table
diesel migration generate create_thread_table
# it will create 4 files under migrations folder
Creating migrations/2022-11-29-063058_create_forum_table/up.sql
Creating migrations/2022-11-29-063058_create_forum_table/down.sql
Creating migrations/2022-11-29-070638_create_thread_table/up.sql
Creating migrations/2022-11-29-070638_create_thread_table/down.sql

Now let's create sql migration for create_forum_table.

--- file: migrations/2022-11-29-063058_create_forum_table/up.sql
CREATE TABLE forums (
	id INT AUTO_INCREMENT PRIMARY KEY,
	question TEXT NOT NULL,
	created_by INT NOT NULL,
	created_at TIMESTAMP NOT NULL,
	deleted_at TIMESTAMP DEFAULT NULL
);
--- file: migrations/2022-11-29-063058_create_forum_table/down.sql
DROP TABLE forums;

And here's for create_thread_table.

--- file: migrations/2022-11-29-070638_create_thread_table/up.sql
CREATE TABLE threads (
	id INT AUTO_INCREMENT PRIMARY KEY,
	forum_id INT NOT NULL,
	created_by INT NOT NULL,
	content TEXT NOT NULL,
	created_at TIMESTAMP NOT NULL,
	deleted_at TIMESTAMP DEFAULT NULL,
	FOREIGN KEY (forum_id) REFERENCES forums(id)
);
--- file: migrations/2022-11-29-070638_create_thread_table/down.sql
DROP TABLE threads;

Now, it times to connect to database and run our migrations. First add this config to .env file.

DATABASE_URL=mysql://localhost:3306/tutorial_rust_api

And run database setup command. With this command diesel will create table __diesel_schema_migratiohns table to store all the information about our databse migrations.

diesel database setup

And now we also need to generate another setup to store our config. It will create a config file to give diesel information where to store the schema and migrations files.

diesel setup

The config file name is diesel.toml:

# For documentation on how to configure this file,
# see https://diesel.rs/guides/configuring-diesel-cli

[print_schema]
file = "src/schema.rs"

[migrations_directory]
dir = "migrations"

And now let's add mysql connection url to .env file like this.

DATABASE_URL=mysql://localhost:3306/tutorial_rust_api

If your connection server need a username and password the env would be like this.

DATABASE_URL=mysql://[username@password]localhost:3306/tutorial_rust_api

So, now let's run the migrations.

diesel migration run
# will print if success.
Running migration 2022-11-29-063058_create_forum_table
Running migration 2022-11-29-070638_create_thread_table

After this migration run, it also will generate schema file in src/schema.rs. This file is auto generated so don't edit it because it will be replaced every time we run the database migrations.

// @generated automatically by Diesel CLI.

diesel::table! {
    forums (id) {
        id -> Integer,
        question -> Text,
        created_by -> Integer,
        created_at -> Timestamp,
        deleted_at -> Nullable<Timestamp>,
    }
}

diesel::table! {
    threads (id) {
        id -> Integer,
        forum_id -> Integer,
        created_by -> Integer,
        content -> Text,
        created_at -> Timestamp,
        deleted_at -> Nullable<Timestamp>,
    }
}

diesel::joinable!(threads -> forums (forum_id));

diesel::allow_tables_to_appear_in_same_query!(
    forums,
    threads,
);

Querying the database

Let's add a couple more packages before we start coding the ORM to handle datetimes and json data. The package chrono is used for working with dates, and serde is used for serializing and deserializing JSON data.

cargo add serde serde_json chrono

Now let's write the orm, let see how actually to use the diesel orm in Rust code. First let's create module to do the database stuff. First create Rust code for storing the entity, let's also call it entity.

// file: src/entity.rs

Writing endpoints

Testing the API

  • Using Postman to test the API
  • Troubleshooting

Conclusion

When it come to building web application there's a lot we need to do, but it will take a lot of time if we want to handle everything from scratch. This is why programming language like PHP, Nodejs, Python, Ruby and other is the best choice for building web application.

But if we need to build fast web application there's would be some limit for those mainstream language. The other options when you want to build high performance web aplication is C or C++ these days.

But the problem with this language is that it's hard to work with if you coming from PHP or JavaScript, we all use package manager all the time it saves a lot of our time. And there's no package management for C or C++ or there's no official package management for this language.

One of the best solution you can chose if you want to build high performance web application is using Rust, it's come with good package manager, growing community, and also it's already used by big company that handle critical service.