Working with a Database

For our project we will use a PostgreSQL database.

You may be already thinking about how to provision that database both locally and in the cloud, and the amount of work that it will take to do so. But no worries, we will use Shuttle to do that for us.

Using Shuttle Shared Databases

Open this link to the Shuttle Docs and follow the instructions to create a shared database in AWS.

As you will be able to see, just by using a macro we will be able to get a database connection injected into our code and a database fully provisioned both locally and in the cloud.

So let's get started!

Adding the dependencies

Go to the Cargo.toml file in the api > shuttle folder and add the following dependencies to the ones you already have:

[dependencies]
...
# database
shuttle-shared-db = { version = "0.36.0", features = ["postgres"] }
sqlx = { version = "0.7", default-features = false, features = [
    "tls-native-tls",
    "macros",
    "postgres",
    "uuid",
    "chrono",
    "json",
] }

Cargo Dependencies

If you want to learn more about how to add dependencies to your Cargo.toml file, please refer to the Cargo Docs.

We are adding the shuttle-shared-db dependency to get the database connection injected into our code and the SQLx dependency to be able to use the database connection.

Note that the SQLx dependency has a lot of features enabled. We will use them later on in the project.

Features

If you want to learn more about features in Rust, please refer to the Cargo Docs.

Injecting the database connection

Now that we have the dependencies, we need to inject the database connection into our code.

Open the main.rs file in the api > shuttle > src folder and add the following code as the first parameter of the actix_web function:

#![allow(unused)]
fn main() {
 #[shuttle_shared_db::Postgres()] pool: sqlx::PgPool,
}

The function should look like this:

#![allow(unused)]
fn main() {
#[shuttle_runtime::main]
async fn actix_web(
    #[shuttle_shared_db::Postgres()] pool: sqlx::PgPool,
) -> ShuttleActixWeb<impl FnOnce(&mut ServiceConfig) + Send + Clone + 'static> {
    let config = move |cfg: &mut ServiceConfig| {
        cfg.service(hello_world);
    };

    Ok(config.into())
}
}

Let's build the project. We will get a warning because we're not using the pool variable yet, but we will fix that in a moment.

cargo build

Running the project

Now that we have the database connection injected into our code, we can run the project and see what happens.

cargo shuttle run

You will see that the project is building and then it will fail with the following error:

Docker Error

Docker

The error is telling us that we need to have Docker running in our system.

Let's start Docker and run the project again.

cargo shuttle run

This time the project will build and run successfully.

Local ConnectionString

Note that you will be able to find the connection string to the database in the logs. We will use that connection string later on in the project.

Connect to the database

Try to connect to the database using a tool like DBeaver or pgAdmin.

Commit your changes.

git add .
git commit -m "add database connection"