SeaQL/sea-orm
> An async, dynamic relational ORM for Rust, layered on top of SQLx and SeaQuery — trading Diesel's compile-time query guarantees for runtime flexibility and a familiar Active Record feel.
GitHub repo · Official website · License: Apache-2.0 / MIT (dual)
Overview
SeaORM is an async ORM for Rust, first published in 2021 by the SeaQL group1. It targets developers building web services (REST, GraphQL, gRPC) who want an ORM whose ergonomics resemble ActiveRecord, Django, or TypeORM rather than a lower-level query builder. As of 2026 it is one of the two most-used ORMs in the Rust ecosystem, the other being Diesel, and it is the async-first option of the pair.
The defining tension is compile-time verification versus runtime flexibility. Diesel checks your schema and queries against Rust's type system at compile time and is synchronous by default; SeaORM builds SQL dynamically at runtime, which makes conditional query construction, generic repositories, and multi-backend code far easier to write, at the cost of some classes of error only surfacing when a query actually runs. SeaORM does not fully invent this stack — it sits on sqlx for the async driver and connection-pool layer and on sea-query for query construction, adding the entity/model/relation abstraction on top2.
The other structural fact worth knowing up front is that SeaORM is one product in a larger commercial ecosystem (Seaography for GraphQL, SeaORM Pro admin panel, a paid SQL Server port). The core ORM is open source under a permissive dual license, but some of the surrounding "batteries" are commercial or separately licensed.
Getting Started
# Cargo.toml — you must select a runtime, a TLS backend, and a database
[dependencies]
sea-orm = { version = "1", features = [
"sqlx-postgres",
"runtime-tokio-rustls",
"macros",
] }
use sea_orm::{Database, EntityTrait, ColumnTrait, QueryFilter};
#[tokio::main]
async fn main() -> Result<(), sea_orm::DbErr> {
let db = Database::connect("postgres://user:pass@localhost/mydb").await?;
// Assuming an entity generated by `sea-orm-cli generate entity`
let cakes: Vec<cake::Model> = cake::Entity::find()
.filter(cake::Column::Name.contains("chocolate"))
.all(&db)
.await?;
Ok(())
}
Entities are normally generated from an existing database with sea-orm-cli generate entity, or written by hand and migrated forward. The CLI also drives the migration system (sea-orm-cli migrate).
Architecture / How It Works
SeaORM is a stack of three SeaQL crates plus SQLx:
1. sqlx (external, by launchbadge) — provides the actual async drivers, connection pool, and TLS for Postgres, MySQL, and SQLite. SeaORM does not talk to the database directly; it delegates execution to SQLx. 2. sea-query — a dynamic SQL query builder. This is where find().filter().join() chains are turned into backend-specific SQL strings and bound parameters. 3. sea-orm — the ORM layer: the Entity / Model / ActiveModel / Column / Relation abstraction, derive macros, and the runtime that maps rows to structs.
The core data-model idea is the split between Model (an immutable, fully-populated struct representing a row) and ActiveModel (a mutable, per-field change-tracked struct used for inserts and updates). ActiveModel records which fields are Set, Unchanged, or NotSet, so an update writes only the columns you actually touched rather than the whole row. This is the mechanism behind SeaORM's Active Record–style .insert(), .update(), and .save() methods.
Relations are declared on entities (1-1, 1-N, M-N, self-referential). Eager loading is explicit: find_also_related for 1-1, find_with_related for 1-N and M-N, and the loader-based APIs to avoid the N+1 problem on nested queries. Because query construction is dynamic, SeaORM cannot verify at compile time that a column exists or that a join is valid — those are runtime DbErrs.
Migrations are written as Rust code (a Migration trait with up/down using the SeaQuery schema builder), not as raw .sql files by default. This keeps migrations backend-agnostic but means schema changes live in Rust and are compiled.
SeaORM 2.0, in release-candidate phase through 2025–2026, reworks the entity format (a denser macro-driven form), adds an entity-first workflow that can diff entities against the database and sync schema, and a synchronous sea-orm-sync variant that drops the async runtime requirement for CLI/SQLite use3. Treat 2.0 APIs shown in the current README as not-yet-final.
Production Notes
Feature flags are load-bearing and a common first-day footgun. You must simultaneously pick a runtime (runtime-tokio-* or runtime-async-std-*), a TLS backend (-rustls or -native-tls), and one or more database backends (sqlx-postgres, sqlx-mysql, sqlx-sqlite). Omitting or mixing these produces confusing linker/compile errors rather than a clear message. The runtime you choose must match the async runtime your web framework uses.
No compile-time query checking. Unlike Diesel, or SQLx's query! macros, SeaORM validates nothing about your SQL at compile time. A typo'd column, a bad cast, or a join against the wrong table compiles fine and fails at runtime. Integration tests against a real database are effectively mandatory; a type-checked build is not evidence the queries work.
It inherits SQLx's operational characteristics. Connection pooling, statement caching, prepared-statement behavior, and TLS quirks are SQLx's, not SeaORM's — when debugging connection exhaustion or TLS handshake failures, read SQLx docs and issues, not just SeaORM's.
Migrations are code, and compile time compounds. Because migrations and entities are Rust, a large schema adds to build times, and migrations ship as part of a compiled binary (or the CLI). Teams coming from Rails/Django SQL-file migrations should expect a different mental model.
Pre-1.0 churn, then stability, then 2.0. The 0.x line (2021–2024) had frequent breaking releases; APIs moved often. 1.0 (2024) stabilized the surface. 2.0 introduces another entity-format change, so pinning a major version and reading the migration guide before upgrading is warranted rather than tracking latest blindly.
Enum and custom-type mapping (Postgres enums, arrays, JSON) works but requires deriving the right traits and matching feature flags; this is a recurring source of issues for non-trivial schemas.
When to Use / When Not
Use when:
- You are building an async Rust web service and want ORM ergonomics (relations, Active Record, batteries) over hand-written SQL.
- You need dynamic query construction — conditional filters, generic repositories, runtime-decided joins.
- You want the same code to target Postgres, MySQL, and SQLite with minimal changes.
- You want an adjacent instant-GraphQL path (Seaography) or an admin panel (SeaORM Pro).
Avoid when:
- You want compile-time guarantees that every query matches the schema — use Diesel or SQLx's checked macros.
- You are writing a synchronous CLI or a context with no async runtime (though
sea-orm-syncin 2.0 narrows this gap). - Your workload is mostly complex, hand-tuned analytical SQL — a thin query layer will fight you less than an ORM.
- You need a stable, rarely-changing API and are unwilling to track a major-version migration.
Alternatives
- diesel-rs/diesel — use instead when you want compile-time-checked queries and a mature, synchronous-first ORM, and can accept less dynamic query building.
- launchbadge/sqlx — use instead when you want async, compile-time-checked raw SQL and no ORM abstraction; it is also the layer SeaORM builds on.
- SeaQL/sea-query — use instead when you want SeaORM's dynamic query builder without the entity/ORM layer.
- rbatis/rbatis — use instead when you prefer a mapper/XML-and-macro style ORM closer to MyBatis.
- loco-rs/loco — use instead when you want a full Rails-like framework; it embeds SeaORM rather than replacing it.
History
| Version | Date | Notes |
|---|---|---|
| 0.1 | 2021-08 | First public release; async ORM on SQLx + SeaQuery1. |
| 0.x | 2021–2024 | Rapid iteration; frequent breaking changes across the 0.x line. |
| 1.0 | 2024-08 | First stable major release; API surface stabilized4. |
| 2.0 (RC) | 2025–2026 | New dense entity format, entity-first schema sync, sea-orm-sync; release candidate, not finalized3. |
References
- ^ SeaQL, "SeaORM 0.1.0 — 🐚 An async & dynamic ORM for Rust." https://www.sea-ql.org/blog/2021-08-30-sea-orm-0.1.0/
- ^ SeaORM documentation — Internal design (relationship to SeaQuery and SQLx). https://www.sea-ql.org/SeaORM/docs/internal-design/
- ^ SeaQL blog, "A walk-through of SeaORM 2.0" and "How we made SeaORM synchronous" (2025–2026). https://www.sea-ql.org/blog/
- ^ SeaQL blog, "SeaORM 1.0" release announcement. https://www.sea-ql.org/blog/
Tags
rust, orm, database, sql, postgres, mysql, sqlite, async, sqlx, active-record, web-services