Skip to content

Diesel

Databases & CachingDatabase/ORMRust

What it is

Diesel is a safe, extensible ORM and query builder for Rust that catches invalid queries at compile time through its type-level schema representation.

diesel_cli generates a schema module from migrations. Queries are built from those generated table types, so column names and types are checked by the compiler.

Installation

cargo add diesel --features postgres,chrono

Getting started

The smallest useful thing you can do with it, and what each part means.

Schema, model and query
// @generated by diesel_cli into src/schema.rs
diesel::table! {
    books (id) {
        id -> Int4,
        title -> Varchar,
        year -> Int4,
    }
}

#[derive(Queryable, Selectable)]
#[diesel(table_name = crate::schema::books)]
struct Book { id: i32, title: String, year: i32 }

use crate::schema::books::dsl::*;

let results = books
    .filter(year.gt(1990))
    .order(year.desc())
    .limit(10)
    .select(Book::as_select())
    .load(conn)?;

// books.filter(titel.eq("x"))  // compile error: no such column
The generated schema module is what makes this work. A typo in a column name is a name-resolution error, not a runtime SQL failure.

Advanced usage

Where the library earns its place over a simpler alternative.

Inserts, joins and transactions
#[derive(Insertable)]
#[diesel(table_name = books)]
struct NewBook<'a> { title: &'a str, year: i32 }

conn.transaction(|conn| {
    let book: Book = diesel::insert_into(books)
        .values(&NewBook { title: "Dune", year: 1965 })
        .returning(Book::as_returning())
        .get_result(conn)?;

    diesel::update(stock.filter(sku.eq(&book.title)))
        .set(quantity.eq(quantity - 1))
        .execute(conn)?;

    diesel::QueryResult::Ok(book)
})?;
The closure returning an Err rolls the transaction back automatically. `quantity - 1` builds SQL arithmetic rather than computing in Rust, so it stays atomic.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

the trait bound is not satisfied on a query
Diesel's errors are notoriously long. Read the first line only: it usually names a column or type mismatch between the struct and the schema.
Column does not exist at runtime
schema.rs is out of date. Re-run diesel migration run and regenerate.

Best practices

  • Regenerate schema.rs with diesel_cli after every migration; a stale schema compiles but lies.
  • Use Selectable and as_select() so struct fields and selected columns cannot drift apart.
  • Prefer SQLx or SeaORM if you need async; Diesel's async story is a separate crate and less mature.
  • Keep migrations in version control and run them in CI against a scratch database.

Background

Why it exists, and what it was reacting to.

Diesel predates async Rust and takes a different approach from SQLx: instead of checking SQL strings against a live database, it represents the schema in the type system, so an impossible join simply does not compile.