Next, we’ll be setting up our database pool, building our models, and connecting our app to Postgres.

First, we’ll import the external modules we need into db.rs

use dotenv::dotenv;

use diesel::pg::PgConnection;

use r2d2;

use r2d2_diesel::ConnectionManager;

use rocket::http::Status;

use rocket::request::{self, FromRequest};

use rocket::{Outcome, Request, State};

use std::env;

use std::ops::Deref;

Next, we’ll create a new type of Pool that implements a Postgres connection

pub type Pool = r2d2::Pool<ConnectionManager<PgConnection>>;

And create a function that returns a Pool.

pub fn create_db_pool() -> Pool {

dotenv().ok();

let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must exist");

let manager = ConnectionManager::<PgConnection>::new(database_url);

r2d2::Pool::new(manager).expect("db pool failure")

}

Here, we’re first making sure that we can access our environment variables. Then, we’re assigning our DATABASE_URL environment variable to a variable. Calling expect() will cause the program to panic and return the string we provide. This is lazy as far as error handling goes, but it will work for our demo. Next, we will create a new Connection Manager that establishes a connection to a Postgres database with the URL we’ll soon supply as an environment variable. Finally, we return a R2D2 pool with our newly-created database manager.

We’ll go ahead and create a Rocket request guard while we’re here too that verifies we’ve connected to the database. You can think of Request Guards as providing extra validation for routes. A common example (one that we won’t go into here) would be verifying that a user is logged in before being able to access a specific route.

pub struct DbConn(pub r2d2::PooledConnection<ConnectionManager<PgConnection>>); impl <'a, 'r> FromRequest<'a, 'r> for DbConn {

type Error = (); fn from_request(request: &'a Request<'r>) -> request::Outcome<DbConn, ()> {

let pool = request.guard::<State<Pool>>()?;

match pool.get() {

Ok(conn) => Outcome::Success(DbConn(conn)),

Err(_) => Outcome::Failure((Status::ServiceUnavailable, ())),

}

}

} impl Deref for DbConn {

type Target = PgConnection;

#[inline(always)]

fn deref(&self) -> &Self::Target {

&self.0

}

}

First, we’re creating another public structure DbConn . Next, we implement FromRequest which basically matches against our application’s state. If there is a pool, we’ll return a successful connection; if not, we’ll return a failure. Notice the 'a and 'r ? Those are Rust lifetimes, which are a whole ‘nother can of worms we won’t open here, but the short of it is that they help keep things in memory as long as we need them.

Before I forget about it, let’s go ahead and create our .env file too, which only needs our database’s URL

DATABASE_URL=postgres://postgres:postgres@localhost/prrr_demo

and we’ll create the database by running diesel setup . After that, we can go ahead and generate our migrations by running diesel migration generate create_cats . You should notice a new /migrations directory at the root of your project and within it another directory with today’s date. We’ll create our down migration

DROP table cats;

followed by our up migration

CREATE TABLE cats (

id SERIAL PRIMARY KEY,

name VARCHAR NOT NULL,

bio TEXT NOT NULL,

kills INTEGER NOT NULL,

image_url VARCHAR NOT NULL

);

and after that, we can go ahead and run them with diesel migration run . Now, our database should be ready with Diesel having done the work for us.

But how do we connect the database to our app? First, we create our schema. The Diesel docs say this in done for us, but it’s easy enough to do on our own.

table! {

cats (id) {

id -> Int4,

name -> Varchar,

bio -> Text,

kills -> Int4,

image_url -> Varchar,

}

}

After that, we create a model for our database in models.rs

use diesel;

use diesel::prelude::*;

use diesel::pg::PgConnection; use schema::cats;

use schema::cats::dsl::cats as all_cats; #[derive(Serialize, Queryable, Debug, Clone)]

pub struct Cat {

pub id: i32,

pub name: String,

pub bio: String,

pub kills: i32,

pub image_url: String,

} #[derive(Serialize, Deserialize, Insertable)]

#[table_name = "cats"]

pub struct NewCat {

pub name: String,

pub bio: String,

pub kills: i32,

pub image_url: String,

}

Notice we have have two structs here. The Cat struct is for retrieving data from the database, while the NewCat (notice it’s lacking an ID) is for inserting a new cat into the database. While we’re here, lets go ahead and add the CRUD methods we’ll be building into our RestAPI.

impl Cat {

pub fn show(id: i32, conn: &PgConnection) -> Vec<Cat> {

all_cats.find(id)

.load::<Cat>(conn)

.expect("Sometimes cats don't come when you call them")

} pub fn all(conn: &PgConnection) -> Vec<Cat> {

all_cats.order(cats::id.desc())

.load::<Cat>(conn)

.expect("Error herding cats")

} pub fn create(cat: NewCat, conn: &PgConnection) -> bool {

diesel::insert_into(cats::table)

.values(&cat)

.execute(conn)

.is_ok()

} pub fn update_by_id(id: i32, cat: NewCat, conn: &PgConnection) -> bool {

use schema::cats::dsl::{

name as n,

bio as b,

kills as k,

image_url as img,

};

let NewCat { name, bio, kills, image_url } = cat; diesel::update(all_cats.find(id))

.set((n.eq(name), b.eq(bio), k.eq(kills), img.eq(image_url)))

.get_result::<Cat>(conn)

.is_ok()

} pub fn delete_by_id(id: i32, conn: &PgConnection) -> bool {

if Cat::show(id, conn).is_empty() {

return false;

}

diesel::delete(all_cats.find(id))

.execute(conn)

.is_ok()

}

}

I feel like these are pretty self-explanatory, so I won’t go into too much detail. A few things to note: