# SQLx: Compile-Time Checked SQL in Pure Rust, Without an ORM

> SQLx is an async Rust SQL crate that verifies your queries against a real database at build time and ships drivers for PostgreSQL, MySQL, MariaDB and SQLite. Here is how the mechanism works, what it costs you in build setup, and when a plain driver is the better call.

**transact-rs/sqlx** — 🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, and SQLite.

- Repository: https://github.com/transact-rs/sqlx
- Stars: 17,514 · Forks: 1,710
- Language: Rust
- License: Apache-2.0
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/transact-rs-sqlx

## The problem SQLx solves for Rust services

Most Rust database code sits at one of two extremes. A raw driver hands you a connection and a string, and a typo in a column name becomes a runtime error in production. An ORM gives you a type-safe model layer, but the generated SQL is something you inspect rather than write. SQLx targets the gap: you keep writing SQL, and the compiler still checks it. The README frames the project as an async, pure Rust SQL crate featuring compile-time checked queries without a DSL, and that last clause is the whole design stance. There is no schema model to keep in sync, no migration DSL, no entity graph. The unit of work is the query string you already had. It is aimed at backend engineers building services on PostgreSQL, MySQL, MariaDB or SQLite who are comfortable in SQL and do not want a second abstraction to learn. The repository also ships a CLI crate, sqlx-cli, in the workspace, which is where the tooling around that promise lives.

## How compile-time checking actually works

The mechanism is a macro that talks to your database during compilation. When you write a query with the checked macros, the macro connects to the database named in DATABASE_URL, asks the server to describe the statement, and uses the returned column types to generate Rust code that decodes each row. A mismatch between the SQL and the Rust struct becomes a compile error rather than a runtime panic. That is why the build needs a reachable database, or a cache prepared ahead of time for environments that have none. The rest of the stack is layered underneath. sqlx-core holds the shared traits, the sqlx::Pool connection pool, and the Row types; sqlx-postgres, sqlx-mysql and sqlx-sqlite are separate driver crates; sqlx-macros and sqlx-macros-core implement the compile-time path. The README notes that the Postgres and MySQL/MariaDB drivers are written in pure Rust with zero unsafe code, and that the crate uses #![forbid(unsafe_code)] unless the sqlite feature is enabled, because the SQLite driver calls the SQLite3 C API through libsqlite3-sys. Rows are streamed and decoded on demand, and the high-level query API prepares and caches statements per connection. Nested transactions with save points, TLS on the network databases, and PostgreSQL LISTEN/NOTIFY are all listed as supported.

## Installing SQLx and running a first checked query

SQLx is a library plus a CLI. The library goes into Cargo.toml, and the README is explicit that you must pick a runtime feature and a TLS feature. The snippet below is the tokio pair with rustls and WebPKI roots, copied from the install section of the README.

```toml
# Cargo.toml
[dependencies]
# PICK ONE OF THE FOLLOWING:

# tokio (no TLS)
sqlx = { version = "0.9", features = [ "runtime-tokio" ] }
# tokio + native-tls
sqlx = { version = "0.9", features = [ "runtime-tokio", "tls-native-tls" ] }
# tokio + rustls with ring and WebPKI CA certificates
sqlx = { version = "0.9", features = [ "runtime-tokio", "tls-rustls-ring-webpki" ] }
```

Once the dependency resolves, the CLI crate in the workspace handles database creation and migrations, and the checked macros read the connection string from DATABASE_URL. Point that variable at a live database before you run cargo build, or the macros have nothing to describe the statement against.

```bash
export DATABASE_URL="postgres://user:password@localhost:5432/mydb"
cargo build
```

On a successful build you get no output from the macros at all; the payoff is that a query naming a column that does not exist fails here rather than at runtime. The README also documents an offline mode for builds with no database, which stores query metadata in a cache you regenerate when the schema or the queries change.

## Where the compile-time promise breaks down

The checking is only as good as the schema the compiler can see. Build in a container with no database and no prepared cache, and the macros cannot describe the statement. The project addresses this with an offline mode that stores query metadata, but that cache is a build artifact you have to regenerate whenever the schema or the queries change, and a stale cache is a silent divergence between what compiled and what the server will run. Two further limits are structural. The README states that MSSQL was supported prior to version 0.7 and has been removed pending a rewrite as part of the SQLx Pro initiative, so SQL Server is not an option here today. And there is no runtime query builder: if your filters, sort columns or table names are chosen by user input, you are concatenating strings yourself, and SQLx will not check the result. That is the trade for having no DSL, and it is worth being honest that the trade cuts both ways.

## SQLx compared with rusqlite and with Go's sqlx

The two nearest neighbours solve different problems. rusqlite is a synchronous SQLite binding, and it is the right shape for a local embedded database inside a CLI, a desktop app or a test fixture where an async runtime is overhead you do not want. SQLx's SQLite driver is async and integrates with the same pool, row and macro machinery as the network drivers, so the same query code can move between SQLite in tests and PostgreSQL in production. If your program never awaits anything else, rusqlite is the smaller dependency. The Go library that shares the name is a different project entirely: it wraps database/sql with reflection-based struct scanning at runtime. SQLx in Rust does the opposite, pushing the check to compile time and generating typed decoders, which is why it needs a database or a cache during the build while the Go version does not.

## Licence, maintenance and the cost of upgrading

The workspace Cargo.toml declares license = "MIT OR Apache-2.0", and the repository root carries LICENSE-MIT and LICENSE-APACHE, so you choose either. That is the standard permissive pair for Rust crates and is compatible with closed-source use; as always, the terms themselves are what govern, not this summary. The workspace version is 0.9.0 and the declared rust-version is 1.94.0, so a pinned older toolchain will fail before it ever reaches a query. The last push to the default branch was on 2026-09-14, and the repository is not archived. Upgrades are not free: the README already warns that the combined runtime-plus-TLS features exist for backward compatibility and may be removed, so code written against the older combined feature names will need editing rather than just a version bump. Budget for reading CHANGELOG.md before each minor upgrade, and expect the sqlx-cli version to move in step with the library.

## Which runtime and TLS combination to pick

This is the decision that trips people up before any SQL is written. The README lists runtime-tokio and runtime-async-std as the runtime features, and notes that Actix-web is fully compatible with Tokio so a separate runtime feature is no longer needed. On the TLS side there is tls-native-tls, which uses OpenSSL on Unix, SChannel on Windows and Secure Transport on macOS, and the rustls family: tls-rustls-ring-webpki, tls-rustls-ring-native-roots and tls-rustls-aws-lc-rs. If you deploy to a minimal container image, the rustls options avoid a system OpenSSL dependency, at the cost of TLS 1.2 and 1.3 only. The README also flags that RSA auth without TLS on MySQL requires the mysql-rsa feature, which is not enabled by the mysql feature. That single line is the difference between a working connection and an authentication failure against a MySQL server configured for caching_sha2_password without TLS.

## Conclusion

Adopt SQLx if you want SQL you wrote yourself, checked against a live schema before the binary ships, and you accept a database or offline cache in the build path. Skip it if you need a query builder to assemble statements at runtime, or if SQL Server support is a requirement, since the README states the MSSQL driver was removed prior to 0.7. Before committing, verify three things in your own tree: that the toolchain meets the workspace rust-version of 1.94.0, that your chosen runtime and TLS feature pair is one the README lists, and that CI can reach a database or a prepared .sqlx cache, because without one of those the macros will not compile.

## FAQ

### What is SQLx in Rust?

It is an async, pure Rust SQL crate for PostgreSQL, MySQL, MariaDB and SQLite, described in its README as featuring compile-time checked queries without a DSL. It is a driver and query layer, not an ORM.

### Does SQLx support SQLite?

Yes. SQLite is one of the four supported databases, with its own driver crate in the workspace. The README notes that the SQLite driver uses the libsqlite3 C library through libsqlite3-sys, which is why the crate only forbids unsafe code when the sqlite feature is off.

### What is a SQLx file?

The README and repository layout do not describe a file format by that name. The project's own tooling is the sqlx-cli crate in the workspace, used for database setup, migrations and preparing the offline query cache.

## Sources

- [Issues](https://github.com/transact-rs/sqlx/issues)
- [License: Apache-2.0](https://github.com/transact-rs/sqlx/blob/main/LICENSE)
- [README](https://github.com/transact-rs/sqlx/blob/main/README.md)
- [transact-rs/sqlx on GitHub](https://github.com/transact-rs/sqlx)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/transact-rs-sqlx
