Rust: building a CRUD REST API
By: Eric Walker
Tags: Programming, Rust, Php Developers, Web Development, Programming Languages

Rust: building a CRUD REST API

Learn how can you build a REST API for CRUD operations with rust and how it compares with other web frameworks

Photo by Markus Spiske on Unsplash

Why Rust?

Rust has been around for about 5 years. Is a language that claims to be “memory-safety” and performs extremely fast. Is a multi-purpose language and comes with built-in functionality for command line, web assembly, networking, and embed applications.

What I want to achieve is to build a CRUD application for an items table. And in the end, compare the performance with an equivalent application built in PHP (lumen).

In the following steps, I am assuming that you already have Rust and Cargo (rustup) fully installed and running.

Migrations with diesel

Diesel is an extensible ORM and query builder for Rust. It’s a productive way to interact with the database because of its abstraction over queries.

Install diesel-CLI to manage migrations. Because we will only use SQLite use the value SQLite in features flag:

cargo install diesel_cli --no-default-features --features sqlite

Setup diesel creates a migration folder and a diesel.toml

# define database url in a dotenv file (diesel needs this)

echo DATABASE_URL=database.db > .env

diesel setup

Now we will generate our migration:

diesel migration generate create_items_table

This last command should create a new folder inside your migrations folder with two files. Up and down dot SQL. In the UP file, you will write the SQL script to create a table. And in the DOWN file write the script to revert your migration. In the up file, I also contain insert to seed the database. The gist bellow does not contain those inserts but you can access it here: https://gist.githubusercontent.com/jorgefsilva/c2f701ae9171a4c67f2e4e97df5ecaf5/raw/b8af2cc529d5124442e817d68c57fb5729c2d8a0/up.sql

Run the migration to create the table in your database:

diesel migration run

Also, run the print-schema command as we will need it for the application

diesel print-schema > src/schema.rs

Directory structure

I believe that having smaller files and separated by responsibility is good for maintenance and is an easier way to organize your projects. So to follow the next steps you should have in your project directory a migrations folder. That was generated by diesel before, and an src directory that will contain our project logic. In src besides our main file, we also have the handlers and models directories and two files. A config file that will set up our environment variables and schema file that was generated by diesel.

In the handlers folder, we have the “controllers” that handles our routes and in models, I think the folder name says it all.

Building the service

The first thing is to write our dependencies in the cargo.toml file:

[package]

name = "rest-api-crud"

version = "0.1.0"

authors = ["Author name <author.name@example.com>"]

edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies]

actix-web = "3"

serde = "1.0.118"

diesel = { version = "1.4.4", features = ["sqlite", "r2d2", "chrono"] }

dotenv = "0.15.0"

r2d2 = "0.8"

chrono = { version = "0.4.10", features = ["serde"] }

Now we will write our main.rs file that is where our application starts:

Here we load all dependencies needed for the application and attach each handler to a route. There 4 routes defined for to create, list all, list one, and delete items.

To load the environment variables there is a config file that builds a Config Struct using the .env file

let config = config::load_config();

Our models and handlers, because they are directories, we must include a mod.rs file in each one. This file allows to include this module in the mains file and should contain only what you want to export:

pub mod items;

In this case, both mod.rs files will only declare items as both modules for handlers and models are called items.

Our items.rs we write our model’s code. We have a model to retrieve the data from the database and another model to insert it. This is because the ID is automatically generated.

The last thing missing is our handlers.

In this items.rs the file is the handler function to list, delete, and create new items.

The API is ready to run with cargo run command.

Performance test

Developing APIs with rust can be very time-consuming when comparing with other languages and frameworks. But rust claims to be super fast and to see how fast we will get all 1000 records from the database. We will do this for 100 consecutive times and calculate the average of the response time.

And using the same database we will compare the same results from an API build in PHP with the lumen framework. I know it’s not a fair fight. But the point is to see how much performance do we compromise when we choose rapid development instead of performance.

To run the performance test I used postman to write a test that makes 100 to the same endpoint and outputs the average response time.

The script above must be pasted in the postman tests tab for the endpoint you want to test.

Result for Rust API:

90 percentile response time 37 is lower than 200, the number of iterations is 100

Result for Lumen API:

90 percentile response time 184 is lower than 200, the number of iterations is 100

Both API does the same thing and returns all of the 1000 records from the database. Rust is incredibly quick by doing it in a quarter of the time that lumen need to do it.

Conclusion

In my opinion, it's not fair to compare lumen with rust. Building the API with lumen took me about 30 minutes. As with Rust, it took a lot more time. Sometimes your development speed is way more important than running speed.

I'm starting to get fascinated with rust and it’s running speed is impressive. I am not sure if it is ready to build large-scale web applications. But for sure can be an alternative for microservices that performance is the most important thing.

Have you already built any web application with Rust? Tell me in the comments.