Diesel Powered Rocket

This holiday season I had decided to rewrite this website (again) to use
Rocket, a new Rust web framework. I had also
thought, "Why not learn some React as well?". While doing so I ran into
all kinds of learning moments and what I made was basically a static site but it was fun!
While there's much to improve I did come up with a fun
little app that I think can show off what Rocket can do. To set it all up we'll need Rocket,
Diesel, Bootstrap and React. The primary point of this demo though is to show how to get Rocket to utilize a thread pool of connections to the database with Diesel.

What does the code do?

It's a very simple page that will display a button, that when clicked
will tell you how many times it has been clicked by users. Pretty simple
but it shows off how to make requests in Rocket and how to use Diesel to
store information.

React Code

My site has routes to different components that when rendered show up
below the navigation bar. We won't be looking at that code today. Instead
we'll be looking at the component that gets rendered when
we go to mgattozzi.com/counting. This component
is called Count and it uses the following code:

import React from 'react';

class Count extends React.Component {

  constructor() {
    super();
    this.state = { count: 0, disabled: false};
    fetch('http://localhost:3000/count')
      .then((response) => response.json())
      .then((responseJson) => {
        this.setState({count: responseJson.count});
      })
      .catch((error) => {
        console.error(error);
      });

    this.clickMe = this.clickMe.bind(this);
  }

  clickMe() {
    this.setState({disabled: true});
    fetch('http://mgattozzi.com/count/', {method: 'PUT'})
      .then((response) => response.json())
      .then((responseJson) => {
        this.setState({count: responseJson.count, disabled: false});
      })
      .catch(console.err);
  }

  render () {
    return(
      <div>
        <div className="panel panel-primary">
          <div className="panel-heading">
            Number of times the button has been clicked
          </div>
          <div className="panel-body text-center">{ this.state.count }</div>
        </div>
        <div>
          <button onClick={this.clickMe}
                  className='btn btn-danger btn-lg center-block'
                  disabled = {this.state.disabled}>
            Click Me!
          </button>
        </div>
      </div>
    );
  }

}

export default Count;

Let's start at the top and work our way down. This component lives in the count.jsx
file in the repo. With my Webpack configuration it turns all of this into JavaScript
automatically. I find the jsx file easier to work with while using React and
I like it a lot in terms of syntax. Plus with Webpack and Babel we get to use all
the new JS syntax goodies! Let's start working through it.

import React from 'react';

class Count extends React.Component {
  // Omitted
}

export default Count;

Here we get to use the new import syntax. We're importing React from
the react package. Webpack figures out how to link this into the code
when it transpiles to JS. In my case it's all the modules installed in
node_modules via the Yarn package manager. We then declare a new class
Count which is what our component is named. It extends the base
Component type from the React library. This means we get all of the
methods and things that Component has! Last but not least we export
the class and say it's the only one being exported from the file. While
I could have added the export default to the beginning of the class
declaration I added it to the end of the file as a personal stylistic
convention.

Okay let's look into our constructor method:

  constructor() {
    super();
    this.state = { count: 0, disabled: false};
    fetch('https://mgattozzi.com/count')
      .then((response) => response.json())
      .then((responseJson) => {
        this.setState({count: responseJson.count});
      })
      .catch(console.err);

    this.clickMe = this.clickMe.bind(this);
  }

First up we call super as part of the setup for constructor. Now we create our
state variables count and disabled. count will be the number
displayed and disabled is for our button. When we click it we
temporarily disable it so that you can't make another request until the
current one is over and the count has been updated.

We then make a request with the fetch API (some older browsers might not
support it) that's built into the browser. We grab the response,
turn it into JSON then set the state from the default value of zero
to the actual count from the server. We then bind this to clickMe so
that it has access to the state inside the function.

  clickMe() {
    this.setState({disabled: true});
    fetch('https://mgattozzi.com/count/', {method: 'PUT'})
      .then((response) => response.json())
      .then((responseJson) => {
        this.setState({count: responseJson.count, disabled: false});
      })
      .catch(console.err);
  }

clickMe simply does a PUT to the server to update the count by one
(we'll see the code for this route soon), and then waits for the
response from the server with the new value. It then changes the count
value and makes the button clickable again. Depending on your
connection speed you might not even see that it becomes disabled!

  render () {
    return(
      <div>
        <div className="panel panel-primary">
          <div className="panel-heading">
            Number of times the button has been clicked
          </div>
          <div className="panel-body text-center">{ this.state.count }</div>
        </div>
        <div>
          <button onClick={this.clickMe}
                  className='btn btn-danger btn-lg center-block'
                  disabled = {this.state.disabled}>
            Click Me!
          </button>
        </div>
      </div>
    );
  }

Last but not lease we have our render function. This is what renders
the Bootstrap panel and button. Anything inside the curly braces is a
apart of the Count component that's added to the output HTML. For
instance { this.state.count } becomes the number value of the count
displayed on screen, the onClick feature runs the clickMe function
on click and disabled is set to true or false by
{this.state.disabled}. It's a simple app for the front end but the
real magic comes from Diesel and Rocket. Let's take a look!

Building a Diesel Rocket

Alright this part is the more complex part. I had originally set this up
using a connection generated each time upon request. This was
inefficient and after looking at this issue
from the Rocket repo I was able to create a version that had a thread pool of database connections
to Postgres using r2d2,
r2d2-diesel and the lazy_static libraries. Let's
first look at the database library I set up for use within my site.

Here's the code for lib.rs:

#[macro_use]
extern crate diesel_codegen;
#[macro_use]
extern crate diesel;
extern crate dotenv;
extern crate r2d2;
extern crate r2d2_diesel;

pub mod schema;
pub mod models;

use diesel::pg::PgConnection;
use r2d2::{ Pool, Config };
use r2d2_diesel::ConnectionManager;
use dotenv::dotenv;
use std::env;

pub fn create_db_pool() -> Pool<ConnectionManager<PgConnection>> {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");
    let config = Config::default();
    let manager = ConnectionManager::<PgConnection>::new(database_url);
    Pool::new(config, manager).expect("Failed to create pool.")
}

A bit overwhelming but let's go through it. First off we import Diesel
and it's macros for use throughout the database library, which I've called mlib
when imported to the binary running the site, then we import all the
other crates we need like dotenv
to read the .env file for the database url, the r2d2 crate for our pool of
connections, and the r2d2-diesel crate for being able to use r2d2 with Diesel's PgConnection type.

After that we publicly declare that we have two modules in the library
that we'll look at soon, they've been labeled schema and models.
Following that it's all the imports we need to create a Pool of database
connections! Let's take a look at that function create_db_pool.

pub fn create_db_pool() -> Pool<ConnectionManager<PgConnection>> {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");
    let config = Config::default();
    let manager = ConnectionManager::<PgConnection>::new(database_url);
    Pool::new(config, manager).expect("Failed to create pool.")
}

First up we read the .env file and set the environment variables in
there. In this case it's the DATABASE_URL variable. We then set that
value as database_url. We now set up the pool config (number of
threads etc.) and as you can see I've opted for the default
configuration here. We now set up the manager of all of our connections
and hand it the database url so that it knows where to go to access the
database. We've also told it we want PgConnections in our pool. Last
but not least we create our Pool of connections and return the value. Neat!

Let's take a look at schema.rs:

infer_schema!("dotenv:DATABASE_URL");

That's it! Diesel does all the hard work figuring out our database
schema for use later. Probably one of my favorite features because this
macro just feels like powerful magic.

Last but not least the models.rs file:

#[derive(Queryable, Debug)]
pub struct Count {
    pub id: i32,
    pub clicks: i32,
}

Since this is the only table in my database my models.rs file isn't
particularly long. We're defining a struct Count that contains an id field
and a clicks field. I only have the id to act as a primary key for
lookup and schema purposes. We just want the clicks value usually! We've
also told the struct to derive the Diesel Queryable trait. This sets
up all the boiler plate code so that we can actually query the database
with it! That's it for the database connection code. Now the actual
binary itself that runs the site!

#![feature(plugin, custom_derive, custom_attribute)]
#![plugin(rocket_codegen)]

#[macro_use]
extern crate lazy_static;
extern crate rocket;
extern crate rocket_contrib;
extern crate diesel;
extern crate mlib;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate r2d2;
extern crate r2d2_diesel;

// Server Imports
// Used to Setup DB Pool
use rocket::request::{Outcome, FromRequest};
use rocket::Outcome::{Success, Failure};
use rocket::http::Status;

// Used for Routes
use rocket::Request;
use rocket::response::NamedFile;
use rocket_contrib::JSON;

// Std Imports
use std::path::{Path, PathBuf};

// DB Imports
use diesel::prelude::*;
use diesel::update;
use diesel::pg::PgConnection;
use r2d2::{Pool, PooledConnection, GetTimeout};
use r2d2_diesel::ConnectionManager;
use mlib::models::*;
use mlib::*;

fn main() {
    rocket::ignite().mount("/", routes![count, count_update, public, static_files, index, site]).launch();
}

// DB Items
lazy_static! {
    pub static ref DB_POOL: Pool<ConnectionManager<PgConnection>> = create_db_pool();
}

pub struct DB(PooledConnection<ConnectionManager<PgConnection>>);

impl DB {
    pub fn conn(&self) -> &PgConnection {
        &*self.0
    }
}

impl<'a, 'r> FromRequest<'a, 'r> for DB {
    type Error = GetTimeout;
    fn from_request(_: &'a Request<'r>) -> Outcome<Self, Self::Error> {
        match DB_POOL.get() {
            Ok(conn) => Success(DB(conn)),
            Err(e) => Failure((Status::InternalServerError, e)),
        }
    }
}

// Routes

// I omitted routes here to not clutter everything up

#[get("/count")]
fn count(db: DB) -> JSON<Clicks> {
    use mlib::schema::counts::dsl::*;
    let result = counts.first::<Count>(db.conn())
        .expect("Error loading clicks");

    JSON(Clicks {
        count: result.clicks,
    })
}

#[put("/count")]
fn count_update(db: DB) -> JSON<Clicks> {
    use mlib::schema::counts::dsl::*;
    let query = counts.first::<Count>(db.conn())
        .expect("Error loading clicks");
    let val = query.clicks + 1;

    update(counts.find(1))
        .set(clicks.eq(val))
        .execute(db.conn())
        .unwrap();

    JSON(Clicks {
        count: val,
    })
}

#[derive(Deserialize, Serialize)]
pub struct Clicks {
    pub count: i32,
}

Okay, that's a lot. I'm going to break this up into quite a few chunks
for digestion of what's going on. First off let's look at main:

fn main() {
    rocket::ignite().mount("/", routes![count, count_update, public, static_files, index, site]).launch();
}

Rocket makes it really easy to just setup routes and run the site. I
don't show all the routes here but most are about just getting the site
via GET requests, nothing particularly interesting. What we care about
are the count and count_update functions. Before that we'll take a look
at how to set up the database for use on those routes.

// DB Items
lazy_static! {
    pub static ref DB_POOL: Pool<ConnectionManager<PgConnection>> = create_db_pool();
}

pub struct DB(PooledConnection<ConnectionManager<PgConnection>>);

impl DB {
    pub fn conn(&self) -> &PgConnection {
        &*self.0
    }
}

impl<'a, 'r> FromRequest<'a, 'r> for DB {
    type Error = GetTimeout;
    fn from_request(_: &'a Request<'r>) -> Outcome<Self, Self::Error> {
        match DB_POOL.get() {
            Ok(conn) => Success(DB(conn)),
            Err(e) => Failure((Status::InternalServerError, e)),
        }
    }
}

Almost all of this I got from the issue I mentioned earlier but I'll
break it down for you:

First off we create a database pool using the create_db_pool function
we went over earlier. Why is it wrapped in a lazy_static! macro call
though? Well to quote the repo the macro is from:


Using this macro, it is possible to have statics that require code to be executed at runtime in order to be initialized.

We want this pool to be available to us for the course of the site's run
time but we can't declare it as being static since we
need to first setup our connections. lazy_static! allows us to do just
that. Neat huh?

Next we create a DB struct that contains one of the connections from
the pool as an inner type. We then create an impl for the DB type
that has a function conn that returns a reference to the PgConnection inside
it for use in our requests.

We now setup an impl of FromRequest, a Rocket trait, for DB which is
what we use in this case to get access to the pool in our routes. The
from_request function returns our DB struct if successful or an
error if it isn't. Cool huh?

Now let's look at the two routes used in the counter:

#[get("/count")]
fn count(db: DB) -> JSON<Clicks> {
    use mlib::schema::counts::dsl::*;
    let result = counts.first::<Count>(db.conn())
        .expect("Error loading clicks");

    JSON(Clicks {
        count: result.clicks,
    })
}

#[put("/count")]
fn count_update(db: DB) -> JSON<Clicks> {
    use mlib::schema::counts::dsl::*;
    let query = counts.first::<Count>(db.conn())
        .expect("Error loading clicks");
    let val = query.clicks + 1;

    update(counts.find(1))
        .set(clicks.eq(val))
        .execute(db.conn())
        .unwrap();

    JSON(Clicks {
        count: val,
    })
}

#[derive(Deserialize, Serialize)]
pub struct Clicks {
    pub count: i32,
}

Alright not too bad in terms of code! You'll notice that both routes
of them have this import in the beginning of the function:

use mlib::schema::counts::dsl::*;

This is what that schema.rs file creates when the macro expands and it
allows us to do things like look things up in the counts table easily!
Okay let's look at Clicks first:

#[derive(Deserialize, Serialize)]
pub struct Clicks {
    pub count: i32,
}

This is the JSON that we return to the front end and it's using serde to
serialize and deserialize to and from JSON for us. We use the custom
derive here to avoid doing any of the boiler plate code again. This will
be wrapped inside of Rocket's JSON type to be returned to the front end
to be used as the new count value displayed to you!

Let's look at our count function:

#[get("/count")]
fn count(db: DB) -> JSON<Clicks> {
    use mlib::schema::counts::dsl::*;
    let result = counts.first::<Count>(db.conn())
        .expect("Error loading clicks");

    JSON(Clicks {
        count: result.clicks,
    })
}

First up you can declare routes with this syntax:

#[get("/count")]

This tells Rocket that the function below it is used to handle GET
requests to the /count endpoint. Now if you look at the function
header you'll see that we have an input called db that's using our DB struct!
This is passed in to the function every time the end point is
requested via a call to from_request. Our query is simple, grab the
first field from the counts table (it's in the sql file I'll show you soon),
and have it return a Count type (the item from models.rs) using a connection retrieved
from having db call conn, and then unwrap that value using expect. In
this case we know the value will be there so this is fine. We then create
a Clicks struct that is serialized to JSON and returned to the
frontend! Simple but really cool stuff. Alright now the PUT request
route to the same endpoint in our count_update function:

#[put("/count")]
fn count_update(db: DB) -> JSON<Clicks> {
    use mlib::schema::counts::dsl::*;
    let query = counts.first::<Count>(db.conn())
        .expect("Error loading clicks");
    let val = query.clicks + 1;

    update(counts.find(1))
        .set(clicks.eq(val))
        .execute(db.conn())
        .unwrap();

    JSON(Clicks {
        count: val,
    })
}

It's similar to before, except now we're saying this route deals with
PUT requests. We query the database for the current count. This time though we
take that returned value and increment it by one. We then find the count field in the
database and set it equal to our val variable and then return that new
value to the front end to update what is displayed! Pretty cool right?

Now let's look at the migration files. Diesel provides a cool cli tool to
setup the database and run migrations to update it. Here's the
sql that was needed to create the table:

CREATE TABLE counts (
  id SERIAL PRIMARY KEY,
  clicks SERIAL NOT NULL
);

INSERT INTO counts (clicks) VALUES (0);

This was all that was needed to get it running. If I messed up while
writing that all I needed to do was revert the change by running
this sql code via Diesel's cli tool.

DROP TABLE counts;

That's all there is too it! Not so bad now was it?

Flaws

I do want to take a moment to point out some flaws in this code.
There might be data races if a bunch of people update the count at the
same time in the database (look at the update query to understand why).
The number therefore might not be the most accurate number.
While this is the case and the code could be made more robust
(not using expect or unwrap) it still accomplishes what I meant to
do which was show Diesel being used in a thread pool with Rocket!

Conclusion

I'm honestly amazed at how easy it was to get this all working once it
made sense. Rocket is fast and a joy to work with compared to my
experience with Iron and the amount of work on making Diesel such a
high quality library that's easy to work with
has been nothing short of incredible. The work that has been put into
both libraries is amazing. I can't thank the authors of all the crates
I used in this one application enough as they made me productive,
rather than hampering my ability to make what I wanted.
If you want to see the button in action and leave a click it's live
here and the code for all
of it is at this tag on my repo here.
Hopefully this gave you a look at what Rocket can do as well as Diesel,
two Rust projects that I really like.

Think my code could use some improvement or you can fix that data race issue I mentioned?
Found a mistake somewhere in the article? Just want to say hi? If so drop by
my repo and open up a PR or
issue! As always, I hope you learned something new today or got some
inspiration to try something out!