Persisting our data via ORM (Diesel)

I think we can all agree, having a few URLs is great but, not too helpful if the data we’re sending isn’t persisted. For this, we’ll use Diesel as it’s currently one of the most mature Rust ORM frameworks.

Doing this for the first time, I’ll admit, is certainly an involved process but once the initial bootstrap is out of the way, it works quite well. I’ve tried this with the blackbeam raw mysql driver, and while it works, the codebase becomes polluted really quick..

In fact, Sean Griffin (author of Diesel) wrote a great article illustrating this very point.

To get started, we install the Diesel CLI:

$ cargo install diesel_cli

Then we tell Diesel where our database should live and run setup:



$ diesel setup

Creating database: heroes $ export DATABASE_URL=mysql://user:pass @localhost/heroes $ diesel setupCreating database: heroes

Next, we’ll generate a migration strategy — this basically allows us to keep revisions as our database evolves over time.

$ diesel migration generate heroes

Creating migrations/2018-03-17-180012_heroes/up.sql

Creating migrations/2018-03-17-180012_heroes/down.sql

We’ll need to edit these two files to include the SQL for our Heroes schema

Now we can run our migration — which will execute up.sql against our DB

[sean@lappy486:~/hero-api] $ diesel migration run

Running migration 2018-03-17-180012_heroes

With any luck, that’s the last SQL we should touch for this project. Let’s go ahead and add Diesel to our dependencies in Cargo.toml. We’re also going to add r2d2-diesel which will allow us to manage db connection pooling.

[dependencies]

diesel = { version = "1.0.0", features = ["mysql"] }

diesel_codegen = { version = "*", features = ["mysql"] }

r2d2 = "*"

r2d2-diesel = "*"

We’ll create src/db.rs to establish our database connection and manage our pool — thankfully, most of this was provided by Rocket Connection Guard starter code

We’ll also need to make the following additions to bind our Hero model with the new table info we’ve created:

This enables us to create an auto-generated schema derived from our struct

$ diesel print-schema > src/schema.rs

However, we’re going to make a small change so that we can leverage the same model for Queryable and Insertable object. The Diesel author expressed fair reasoning for separating our object into two models but, I prefer the convenience and less code pollution. So for that, we edit src/schema.rs 😅

Without the change above, we’d require two models: one to insert (without id) and a separate to retrieve (with id).

Now we can import these new dependencies by adding the following into src/main.rs :

#[macro_use] extern crate diesel;

extern crate r2d2;

extern crate r2d2_diesel; mod db;

mod schema;

Next we’ll tell Rocket to manage our db connection pool by adding the following to our rocket::ignite() chain:

.manage(db::connect())

Believe it not, now we’re ready to get this thing going! Here we’ll expose a few methods by creating an implementation for Hero within src/hero.rs — save for more sophisticated error handling.

Now we can leverage the newly created methods within our route handlers

Notice, each method within our route handlers and Hero implementation now essentially accept the same/similar arguments: Some combination of an id , Hero object and database connection .

For more info on how to query data, refer to the Diesel Getting Started guides.

Finally, we can now create a Hero and see real results in our REST Client. Fetching, updating and deleting Heroes also work as expected.