|claim|login
RepoCritics — Review. Share. Archive. Every open-source repo.

diesel-rs/diesel

Wiki: diesel-rs/diesel

Source: https://github.com/diesel-rs/diesel

Last synced 2026-07-16 · 1197 words · Edit wiki on GitHub →

diesel-rs/diesel

> A compile-time-checked ORM and query builder for Rust — synchronous by design, with SQL correctness enforced by the type system rather than at runtime.

GitHub repo · Official website · License: MIT OR Apache-2.0

Overview

Diesel is a query builder and ORM for Rust that pushes SQL correctness into the type system. Where most ORMs discover a malformed query, a type mismatch, or a missing column at runtime, Diesel rejects it at compile time: a column that does not exist, a WHERE clause comparing an Integer to a Text, or a SELECT whose shape does not match the target struct are all compiler errors1. It was created by Sean Griffin (formerly on the Rails/ActiveRecord core team) around 2015, and has for years been maintained primarily by Georg Semmler (weiznich)2. It supports three backends: PostgreSQL, MySQL, and SQLite.

The defining tension in Diesel is synchronous execution. Diesel's core Connection API is blocking, and this was a deliberate design choice made before Rust's async story matured. In an async web service (Actix, Axum, Tokio), you either run Diesel calls on a blocking thread pool (tokio::task::spawn_blocking plus an r2d2 connection pool) or reach for the separate diesel-async crate. This makes Diesel unusual in the modern Rust ecosystem, where sqlx and sea-orm are async-first.

The second tension is compile-time cost paid twice: once in build time (the type-level query encoding is heavy on the compiler) and once in developer legibility (Diesel's type errors are among the most intimidating in the Rust ecosystem). The payoff is that a Diesel program that compiles is very unlikely to issue a malformed query.

Getting Started

# Cargo.toml — pick exactly the backends you need
[dependencies]
diesel = { version = "2", features = ["postgres"] }
# The CLI manages migrations and generates schema.rs
cargo install diesel_cli --no-default-features --features postgres
diesel setup
diesel migration generate create_users
use diesel::prelude::*;

#[derive(Queryable, Selectable)]
#[diesel(table_name = crate::schema::users)]
struct User {
    id: i32,
    name: String,
    hair_color: Option<String>,
}

fn load_users(conn: &mut PgConnection) -> QueryResult<Vec<User>> {
    use crate::schema::users::dsl::*;
    users
        .filter(hair_color.eq("Black"))
        .select(User::as_select())
        .load(conn)          // blocking call
}

schema.rs — the table! definitions that back the DSL — is generated by the CLI (diesel print-schema) from the live database, not hand-written.

Architecture / How It Works

Diesel's query builder is a type-level encoding of a SQL AST. Each builder method (.filter(), .select(), .order(), .limit()) returns a new, distinct Rust type that represents the query-so-far. When you finally call .load() / .execute() / .get_result(), the QueryFragment trait walks that type and emits backend-specific SQL, and the Queryable/QueryableByName traits deserialize rows into your structs.

Key pieces:

  • table! macro / schema.rs — generates a module per table with typed column constants. This is the source of truth the DSL type-checks against. It must be regenerated (or migrated) whenever the schema changes.
  • DerivesQueryable (read a row positionally), Selectable (map a struct to an explicit column list, avoiding SELECT * positional bugs), Insertable, AsChangeset (for UPDATE), and Identifiable/Associations (for belonging_to joins).
  • Backend traitPg, Mysql, and Sqlite implement Backend; query fragments are specialized per backend at compile time, so PostgreSQL-only features (arrays, RETURNING, upsert on conflict) simply do not compile against SQLite.
  • into_boxed() — because each query is a distinct type, conditionally-built queries do not unify. .into_boxed() erases the type into a single boxed query so you can add filters in a loop or behind an if.

Because the query is a Rust value with a concrete type, joins, subqueries, and aggregates are all checked for referential validity: you cannot filter by a column from a table that is not in the FROM clause. The cost is that the compiler carries a large type per query, which is the direct cause of both slow builds and the multi-screen error messages.

Production Notes

Async is not native. In an async service, a raw Diesel call will block the executor thread. The two supported patterns are: (1) spawn_blocking + an r2d2 pool sized to your blocking thread pool, or (2) the separate diesel-async crate, which provides AsyncPgConnection and AsyncMysqlConnection (no SQLite) with deadpool/bb8 pools3. Mixing the two, or forgetting spawn_blocking, is the most common Diesel-in-production mistake.

Type errors are a real onboarding cost. A single wrong column type can produce hundreds of lines of trait-bound errors mentioning internal types (SelectStatement, AppearsOnTable, Nullable). The practical mitigation is to change one query method at a time and lean on Selectable/as_select() rather than positional Queryable, which localizes the error to the mismatched field.

Compile times. Large schemas (hundreds of tables) and many distinct queries inflate build time noticeably, since each query is monomorphized. Splitting the data layer into its own crate so it recompiles independently is the standard workaround.

schema.rs drift. The generated schema and the actual database can diverge if migrations are applied out of band. diesel print-schema (or diesel migration run which can auto-regenerate) must be part of the workflow; a stale schema.rs produces confidently-wrong compile-time guarantees.

Backend parity is uneven. PostgreSQL is the most complete backend (arrays, ranges, ON CONFLICT, RETURNING, JSONB). SQLite gained RETURNING support later. MySQL historically lags on some expression features. Feature-flagging the wrong backend produces a compile error, not a runtime surprise — which is the point, but it means porting a query across backends is not free.

2.0 was a breaking migration. Diesel 2.0 (2022) changed Connection methods to take &mut self, reworked the derive attributes to the #[diesel(...)] form shown above, and revised several trait bounds4. Upgrading a 1.x codebase is a mechanical but non-trivial diff across every query site.

When to Use / When Not

Use when:

  • You want the database layer's mistakes to be compile errors, not 3 a.m. pages.
  • Your workload is CPU-cheap-per-query and you can afford a blocking thread pool, or you adopt diesel-async.
  • You target PostgreSQL and want first-class access to its type system (arrays, ranges, upsert).
  • You value a stable, hand-written query DSL over macro-checked raw SQL strings.

Avoid when:

  • You are async-first and unwilling to run a blocking pool or add diesel-async (then sqlx or sea-orm fit better).
  • You need highly dynamic, runtime-constructed queries — Diesel's typed builder fights you, and .into_boxed() only goes so far.
  • Your team is new to Rust and the type-error wall would stall delivery.
  • You need SQLite with async — diesel-async does not cover it.

Alternatives

  • launchbadge/sqlx — async, compile-time-checked raw SQL (macros validate queries against a live database at build time) rather than a typed builder. Use instead when you prefer writing SQL and want native async.
  • SeaQL/sea-orm — async, dynamic, ActiveRecord-style ORM built on sqlx. Use instead when you want runtime-flexible queries and relations without Diesel's type-error curve.
  • weiznich/diesel_async — not a competitor but the async adapter layer over Diesel itself. Use instead of raw Diesel when you need async Postgres/MySQL and want to keep the Diesel DSL.
  • sfackler/rust-postgres (tokio-postgres) — a low-level async Postgres driver with no ORM. Use instead when you want raw control and no query abstraction.
  • rbatis/rbatis — compile-time dynamic-SQL ORM with an async focus. Use instead when you want MyBatis-style templated SQL.

History

VersionDateNotes
1.02018-01First stable release; the typed DSL and CLI as known today1.
1.42019Long-lived 1.x line; widely deployed for years.
2.02022Breaking: &mut connections, #[diesel(...)] attributes, trait reworks4.
2.12023Incremental: improved derives, backend fixes.
2.22024Further backend/feature work on the 2.x line.

References

  1. ^ Diesel homepage and getting-started guide. https://diesel.rs/guides/getting-started
  2. ^ Diesel README, "Notable Sponsors and Supporters" and GitHub Sponsors for weiznich. https://github.com/diesel-rs/diesel
  3. ^ diesel-async crate — async connection implementations for Diesel (Postgres/MySQL). https://github.com/weiznich/diesel_async
  4. ^ Diesel 2.0 migration guide. https://github.com/diesel-rs/diesel/blob/master/guide_drafts/migration_guide.md

Tags

rust, orm, query-builder, database, postgresql, mysql, sqlite, sql, type-safe, compile-time