Open-source project
diesel-rs/diesel_async avatar
diesel-rs/diesel_async

diesel-async makes Diesel's query DSL work in an async runtime

Diesel async connection implementation

828 stars124 forksRustApache-2.0

At a glance

What is it?
diesel-async is an async implementation of Diesel's connection layer and query methods for Rust, designed as a drop-in replacement that keeps Diesel's own query builder intact. It covers PostgreSQL, MySQL and SQLite, adds streaming queries, and ships pool integrations for deadpool, bb8 and mobc.
Who is it for?
diesel-async suits a Rust team that has committed to Diesel and needs to run its queries inside an async runtime, since the query DSL, the schema macros and the model derives stay exactly where they were while only the connection and the awaiting change. It is the wrong tool for a project that has not chosen an ORM, or one that needs a database Diesel does not support, and it carries the usual cost of an adapter layer, where a version bump in Diesel can break the pairing.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Diesel stays, only the awaiting changes

diesel-async provides an async implementation of Diesel's connection implementation and of any method that may issue a query. The design goal is stated as a pure async drop-in replacement for the corresponding Diesel methods.

What that means in practice is visible in the simple usage example. The Cargo manifest needs both crates, with no backend features on Diesel itself:

toml
[dependencies]
diesel = "2.3.0" # no backend features need to be enabled
diesel-async = { version = "0.7.0", features = ["postgres"] }

The query itself is ordinary Diesel. The table macro, the Queryable and Selectable derives and the filter chain are unchanged. What changes is the connection type and the addition of await:

rust
use diesel::prelude::*;
use diesel_async::{RunQueryDsl, AsyncConnection, AsyncPgConnection};

and then the query executes through the provided async RunQueryDsl trait, with .load(&mut connection) followed by .await.

That is the whole argument for the crate. If your team already knows Diesel's query builder, the type safety it gives and the compile time checking of your SQL, then swapping the synchronous execution model for an asynchronous one should not force you to relearn the query layer.

Three backends, and no features enabled by default

The supported databases are PostgreSQL, MySQL and SQLite, and each has a feature flag that turns on its connection implementation. The postgres flag enables AsyncPgConnection, the mysql flag enables AsyncMysqlConnection, and there are three more flags for the connection pool integrations.

By default no features are enabled, which means adding the crate on its own compiles an empty shell. You choose the backend at the point where you declare the dependency, and that is visible in the manifest example above where the postgres feature is switched on.

The Cargo manifest also shows how the dependencies are arranged. The PostgreSQL path uses tokio-postgres 0.7.10 and tokio 1.26, the MySQL path uses mysql_async 0.37 with the minimal-rust feature, and the futures crates at 0.3.17 supply the core, channel and utility pieces. The diesel dependency is pinned with a tilde to the 2.3 series and is pulled in with default-features off plus the third party backend feature, so the async crate does not drag in a synchronous backend that would conflict with it.

The crate metadata also declares a rust-version of 1.88.0 and the 2021 edition, and the package description calls it an async extension for Diesel the safe, extensible ORM and query builder.

Transactions roll back on error, with a boxed closure

The transaction interface is described as an ergonomic way to wrap several statements into a shared database transaction, with automatic rollback as soon as the inner closure returns an error.

The shape of it is a closure passed to connection.transaction, and the closure body is itself an async block that has to be boxed. The scope_boxed call is what makes that work, and the error type is named explicitly in the turbofish:

``` rust connection.transaction::<_, diesel::result::Error, _>(|conn| async move { diesel::insert_into(users::table) .values(users::name.eq("Ruby")) .execute(conn) .await?;

let all_names = users::table.select(users::name).load::<String>(conn).await?; Ok(()) }.scope_boxed() ).await?; ```

The boxed closure is the one piece of friction in the whole API. It exists because the transaction body must be able to borrow the connection for an unknown lifetime, and the alternative, a macro that takes the body, would change how your code is written rather than how it is called. The diesel result error type is threaded through the signature so that your error handling stays in one type.

Inside the closure, statements are still Diesel's insert_into, values and select calls, with the await added. So the transaction body reads like Diesel code with two extra keywords rather than a different style.

Streaming lets you process rows as they arrive

Beyond loading a whole result into a vector, the crate can return a value stream for each query, which allows processing data from the database while it is still being received.

The API is a single method change. load_stream instead of load, and the return type becomes a stream of results rather than a vector:

rust
// use ordinary diesel query dsl to construct your query
let data: impl Stream<Item = QueryResult<User>> = users::table
    .filter(users::id.gt(0))
    .or_filter(users::name.like("%Luke"))
    .select(User::as_select())
    // execute the query via the provided
    // async `diesel_async::RunQueryDsl`
    .load_stream(&mut connection)
    .await?;

This is the feature that has no synchronous equivalent in the same shape, and it is the reason to reach for an async database layer at all when the result set is large. A vector of results means the entire result set is buffered in memory before your code sees the first row, and for a report query over millions of rows that is the difference between a working endpoint and an out of memory failure. With a stream, each row is processed as it arrives and the connection is released when you stop pulling.

The trade is that the stream type has to be named in the binding, as the impl Stream annotation shows, and the error type per item is QueryResult rather than a single Result at the end.

Three pool implementations behind one manager type

Connection pooling is built in, with support for deadpool, bb8 and mobc, and the shape is the same across all three. A manager type, AsyncDieselConnectionManager, is parameterised by the connection type, and each pool has its own module path under diesel_async::pooled_connection.

With deadpool, the configuration is created first and then handed to the pool builder, and the checkout is awaited:

``` rust use diesel_async::pooled_connection::AsyncDieselConnectionManager; use diesel_async::pooled_connection::deadpool::Pool; use diesel_async::RunQueryDsl;

// create a new connection pool with the default config let config = AsyncDieselConnectionManager::<diesel_async::AsyncPgConnection>::new(std::env::var("DATABASE_URL")?); let pool = Pool::builder(config).build()?;

// checkout a connection from the pool let mut conn = pool.get().await?;

// use the connection as ordinary diesel-async connection let res = users::table.select(User::as_select()).load::(&mut conn).await?; ```

With bb8 the builder is asynchronous, so the pool is built with an await, and with mobc the pool is constructed directly with the config. Once a connection is checked out, the usage is identical in all three cases, and the connection behaves like an ordinary diesel-async connection.

Having three backends is unusual for a crate this size. The reason is presumably that pool choice tends to be a project wide decision made elsewhere in a stack, and a database layer that only supported one pool would force that decision on every user. The cost is that three slightly different construction APIs have to be tested and documented.

TLS certificates and the sync wrapper example

One operational note appears under the heading about using the crate with a secure database. With an sslmode=require flag, it is necessary to build a TLS certificate, and an example is provided for doing that using the rustls crate in the postgres examples folder.

That is a small but real point of friction. Async Rust database stacks frequently need the TLS backend chosen explicitly rather than picked from the platform default, and the fact that the example lives in the repository means the pattern is at least copyable.

The example files at the top level are a postgres directory and a sync-wrapper directory. The sync wrapper is worth noting because it goes the other way: it lets synchronous code drive an async connection, which is the usual bridge when a project is partly migrated and a blocking call still has to run a query. The repository also carries a tests directory, a CHANGELOG.md, and the two licence files, LICENSE-APACHE and LICENSE-MIT, matching the dual licensing declared in the manifest as MIT OR Apache-2.0.

A 0.9 line that is moving quickly

The release history shows a project in active development on a fast cycle. Version 0.9.2 was tagged on 2026-06-19, 0.9.1 on 2026-06-05, and 0.9.0 on 2026-04-30. The repository is not archived and the last push was on 2026-10-07.

Note the gap between the example in the README and the current release. The dependency snippet shows diesel-async at version 0.7.0 with the postgres feature, and the current published version is 0.9.2. That is a two minor version gap, which in a 0.x crate means the API may have moved since the README was written, so the version you copy from the front page is not the version you should pin.

The manifest is where to get the real constraint. The diesel dependency there is pinned to the 2.3 series with a tilde, so a Diesel upgrade within that range is expected to work, while a Diesel major bump is not.

The licence is the standard Rust dual of Apache-2.0 and MIT, and the contributing section asks people to open a discussion before submitting a design change, which is a sensible practice for a crate that other projects build their data layer on.

Editorial conclusion

diesel-async suits a Rust team that has committed to Diesel and needs to run its queries inside an async runtime, since the query DSL, the schema macros and the model derives stay exactly where they were while only the connection and the awaiting change. It is the wrong tool for a project that has not chosen an ORM, or one that needs a database Diesel does not support, and it carries the usual cost of an adapter layer, where a version bump in Diesel can break the pairing. Before adopting it, add diesel and diesel-async with the backend feature you need, run a single query through AsyncPgConnection::establish, and check the CHANGELOG for each Diesel release you upgrade past.

Frequently asked questions

How do I add diesel-async to a Rust project?

Add both crates, with no backend features on Diesel and the backend feature on diesel-async. The README shows diesel = "2.3.0" and diesel-async = { version = "0.7.0", features = ["postgres"] }, and the current published release is 0.9.2.

Which databases does diesel-async support?

PostgreSQL, MySQL and SQLite. Each has a feature flag, with postgres enabling AsyncPgConnection and mysql enabling AsyncMysqlConnection, and by default no features are enabled at all.

Can diesel-async stream query results instead of loading them all?

Yes. The load_stream method returns a value stream for each query, which allows processing data from the database while it is still received, instead of buffering the whole result set in a vector.

Official sources

  1. diesel-rs/diesel_async on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/diesel-rs-diesel-async.svg)](https://hysenlabs.com/projects/diesel-rs-diesel-async)

Was this useful?