How to Use Any SQL Database With Rust

Rust is emerging as a frontrunner for ensuring memory safety without sacrificing performance. Its growing popularity isn’t solely based on the “fearless concurrency” mantra but also on its expanding ecosystem that fosters integration with various technologies. A domain Rust proves to be formidable is database interaction, and a pivotal player in this realm is the Diesel ORM. Diesel is an ORM and query builder designed with the philosophy of type safety and a simple API that primes application performance.

In this article, I will demonstrate how to get up and running with Rust, connecting to SQL databases using the Diesel ORM.

Getting Started Using SQL Databases With Rust

You’ll need to download and install Rust and your preferred SQL database management system to interact with SQL databases through Rust. Diesel. In this tutorial, we will use SQLite as the DBMS, but you can use your preferred relational database

After you’ve installed Rust and a preferred SQL DBMS that Diesel supports, you can proceed to create a new Rust project with Cargo’s init command:

The cargo init command initializes the new project and adds a cargo.toml file in the root directory for dependency management.

Next, you’ll add Diesel as a dependency and install the diesel_cli tool to start working with databases.

You need these tools to set up and use an SQL database with Diesel.

Database Setup With Diesel

Setting up a database and connecting to a database with Diesel is a straightforward process.

First, create an environment variables .env file in the root directory of your project and add your database connection string with the KEY DATABASE_URL.

Then, run the diesel setup command to set up the database for your project.

On running the command, Diesel creates a migrations directory, validates the connection to the database, and runs existing migrations.

Here’s how you can setup migrations with the diesel migrations generate command:

The command will generate two SQL files in the migrations directory: up.sql and down.sql.

You can write the SQL for your database table definitions in the up.sql file and the SQL query to drop the tables in the down.sql file.

Here’s an example of content for the up.sql file:

Then your down.sql file should look like this:

After writing the SQL files, you can run the migration run command to run migrations.

You can use the migration redo command to revert previous migrations:

Diesel generates a schema.rs file in your src folder, and you can print the contents of the file with the diesel print-schema command:

The output of the command should match the contents of your src/schema.rs file as thus:

You can connect to your SQLite database with Diesel’s SqliteConnection::establish function that takes in the database URL and connects to the database:

The establish_connection() function connects to the database and returns an SQLite connection instance that you can use for operations on the database.

Basic CRUD Operations

CRUD (Create, Read, Update, Delete) operations form the basis for most database operations, so you’ll need to understand how to perform those operations to grasp the fundamentals of working with Diesel.

Here’s the list of imports and statements you’ll need throughout the CRUD operations.

The diesel::prelude::* statement imports most of Diesel’s functionalities.

Creating (Inserting) Records into a Database

Diesel provides aninsert_into function that you can use to insert data into your database.

Here’s a struct that matches the data you’d want to insert. Notice the macro definitions for the operation Insertable and table_name = schema::human.

You’ll initialize the NewHuman struct and use the instance to insert data into the database as thus:

The main function establishes a connection to the database with the establish_connection function and inserts the new_human struct instance into the database with the insert_into function’s values function.

Here’s the result of the insert operation:

Querying (Reading) Data From the Database

The struct for query operations will differ from the struct for insert operations, especially the Derive macro.

Here’s the struct for an insert operation into the Human table:

You can use the load method on the table to load all the entries in the human table:

Here, the main function loads all the entries in the database with the load method after establishing a connection to the database. The main function prints the entries to the console.

Updating Database Entries

You’ll need to implement the AsChangeset derive macro for your update struct to update entries in the database.

The UpdateHuman struct is the struct for update operations on the human table.

You’ll use the update function to update entries in your database tables:

The main function uses an id to search the database with the find function on the table instance before proceeding with the update operation if the entry exists. The diesel::update function updates the table with the new entry with the set function.

Deleting Entries From the Database

You can use the delete function to delete database entries based on an ID.

The main function searches for the entry with the id through the find function that the delete function takes in and executes the delete operation through the established connection.

Popular Database Operations With Diesel

Diesel supports more operations than just CRUD operations. You can also work with multiple tables and perform more complex operations like storing a list of entries in the database:

Storing a List of Entries into the Database

One of the ways you can store a list of entries in the database is using a vector of structs that you’ll iterate over and insert the values.

Here’s how you can insert a vector of three structs (three entries) into the database:

After declaring three struct instances in the new_humans vector, the main function uses a for-range loop to loop through the structs and insert the values into the database with the insert_into function.

Working with multiple tables is similar to working with single tables, except you’ll have to set up migrations for the tables individually.

Here’s how you can generate migrations for two tables named item and item_type, respectively.

Now you can fill each table’s up.sql and down.sql files as usual to create and then drop the structures.

Now run the diesel migration run command to complete the setup process. Your schema.rs file should look like this:

Now that you’ve set up your database migrations, you can proceed to other operations, like inserting data into the tables.

Here’s the list of imports you’ll need for the insert operation.

Additionally, here are the structs you need for the insert operation:

Notice that the structs implementInsertable for Diesel to be able to insert their instances into the database.

You can use Diesel’s insert_into function to insert entries in the tables on instantiation as usual:

The main function inserts data into the item and item_type tables with the insert_into function that takes in the table declarations from the schema.rs file.

Here’s the result of the insert operation on the item table:

Also, here’s the result of the insert operation on the item_type table:

You can query the database for the data from the item and item_type tables.

Diesel provides robust support for writing raw SQL in its sql_query function that you can use for functionalities Diesel doesn’t support.

Here are the imports you need for the query operation, including Diesel’s sql_query function import:

You can declare a struct that matches your schema to query the database with a struct as thus:

The structs implement Debug and QueryableByName for the query operation using raw SQL.

Here’s how you can query the tables with the item ID using raw SQL:

The main function connects to the database with the establish_connection function before the item_type_id_to_retrieve declaration. The string in the sql_query function is the SQL statement for the operation, and you can bind the result to structs with the bind function.

The main function uses a for-range loop to print the fields of the struct instance.

Here’s the result of the operation:

Diesel also provides functionality for code-first relationships on the Relations page that you can check out.

Note: building SQL to execute can lead to errors. Another name for this is dynamic SQL, which can sometimes allow someone to form a string that can cause harm to your database.

Conclusion

You’ve deeply explored how to use Diesel to interact with SQL databases in your Rust apps. You learned how to set up your database, connect to databases, perform CRUD operations, and work with multiple tables, amongst other operations.

You can do so much with Diesel, including building REST APIs. You can use tools like Actix and Rocket to build APIs with Diesel. Since Diesel provides an interface for writing RAW SQL and has excellent community support,