How I built a Formula1 API š - part 1
Thibault Cheneviere
Posted on January 24, 2024
Building a Formula1 REST API in Rust using diesel.rs and rocket.rs.
In this guide I'm going to walk through my process of building a REST API in Rust from scratch. I will talk about my decisions in terms of project architecture, choices of frameworks or crates. I'll try to reflect my reflection process as much as I can. You can follow the development of this project on GitHub.
This guide assumes that you have a basic-to-decent understanding of Rust concepts and the language syntax.
Let's start our journey through this project !
Why a Formula1 API ?
First of all, why did I choose to even build an F1 API ? Well don't you like cars, fast cars ? Well I do, and the only open-source API available, known as the Ergast API is quite cool and complete but is deprecated and will shutdown at the end of year 2024.
So, why not create a new open-source project providing an API to access the same data ? That's how I started this project !
Building the project architecture
Now that you know why I did start this project, let's start by creating our architecture. We'll be using the workspace
feature of Rust and Cargo. This feature allows to have multiple packages inside a same project that share the same Cargo.lock
and target/
folder.
cargo new rust_race_engine
cd rust_race_engine
rm -rf src/
We deleted the src
folder as we don't need it in the top level crate. Next, we're going to create a new project for each layer of the application. We'll have the following layers :
-
api
layer will handle API requests and contain our handlers -
application
layer will handle the logic behind the API requests. It'll contain the implementation of each routes -
infrastructure
layer will hold our migrations and the connection pool structure -
shared
layer will hold any other models needed in our project, for example the responses and parameters models.
cargo new --lib api
cargo new --lib application
cargo new --lib infrastructure
cargo new --lib shared
By the end of these steps, your folder architecture should look like this.
.
āāā Cargo.lock
āāā Cargo.toml
āāā api
ā āāā Cargo.toml
ā āāā src
ā āāā lib.rs
āāā application
ā āāā Cargo.toml
ā āāā src
ā āāā lib.rs
āāā infrastructure
ā āāā Cargo.toml
ā āāā src
ā āāā lib.rs
āāā shared
āāā Cargo.toml
āāā src
āāā lib.rs
We are now going to link all these projects in the top-level Cargo.toml
file. We'll delete everything inside and enter the following lines :
[workspace]
members = [
"application",
"api",
"infrastructure",
"shared"
]
resolver = "2"
That ends the configuration of our project. Now we can get to work on the actual code.
Database and migrations
Database setup
The first step will be to set up the database and the diesel.rs migrations to use the database in our Rust project. As I plan to recreate an API like the Ergast API, I'll use their open-source database as a starter.
The Ergast website provides MySql dumps of their database. I'll be using docker to host my database in a container. I'll not deep dive into how to use the mysql
image but here is how I start the database server :
docker run \
--name=rust_race_engine \
-d \
-p 3306:3306 \
-v $(pwd)/db/f1db.sql:/docker-entrypoint-initdb.d/dump.sql \
-e MYSQL_DATABASE="f1db" \
-e MYSQL_USER="f1_user" \
-e MYSQL_PASSWORD="formula1" \
mysql/mysql-server:latest
To quickly explain, this hosts the database found in the db/f1db.sql
file, which should be the file downloaded from the Ergast website. The database can then be accessed at the following address : mysql://f1_user:formula1@127.0.0.1:3306/f1db
.
We now have an up and running MySql server.
Migrations
We can now focus on the database migrations. To do so, i'll follow the migrations guide from diesel.rs. I won't repeat everything here so you can refer to it as an additional content.
First, we'll need the diesel_cli
tool. We can install it using this command :
cargo install diesel_cli --no-default-features --features mysql
Next, we'll create a diesel.toml
file in our top-level folder and paste the following content in it :
# For documentation on how to configure this file,
# see https://diesel.rs/guides/configuring-diesel-cli
[print_schema]
file = "application/src/schema.rs"
custom_type_derives = ["diesel::query_builder::QueryId"]
[migrations_directory]
dir = "infrastructure/src/migrations"
We'll also create a folder migrations
inside infrastructure/src
and place a up.sql
file in it. Inside this file we'll put the content of the f1db_tables.sql
file that we can find on the Ergast website. This is the file used to create the tables.
We can now generate our diesel.rs tables with the two following commands :
echo mysql://f1_user:formula1@127.0.0.1:3306/f1db > .env
diesel migration run
If everything works fine you should see a new file inside your application package called schema.rs
containing all diesel.rs tables. We'll be using these tables to create the data models to query the database.
Conclusion
So far we've covered the architecture setup of our project and the migrations of the Ergast API database using diesel.rs.
I hope your learnt a lot with this article and enjoyed it ! Make sure to read the next parts to continue to build this F1 API and don't forget to star the GitHub repository and feel free to give any feedback or suggestions you have.
Thanks for reading and see you in the next part !
Thibault
References
Posted on January 24, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.