Async
Relying on SQLx, SeaORM is a new library with async support from day 1.
Dynamic
Built upon SeaQuery, SeaORM allows you to build complex dynamic queries.
Service Oriented
Quickly build services that join, filter, sort and paginate data in REST, GraphQL and gRPC APIs.
Production Ready
SeaORM is feature-rich, well-tested and used in production by companies and startups.
Who's using SeaORM?
The following startups are using SeaORM:
For more projects, see Built with SeaORM.
A quick taste of SeaORM
- Entity
- Select
- Nested Select
- Insert
- Insert (advanced)
- Update
- Save
- Delete
- Ergonomic Raw SQL
You don't have to write this by hand! Entity files can be generated from an existing database with sea-orm-cli.
use sea_orm::entity::prelude::*;#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]#[sea_orm(table_name = "cake")]pub struct Model {#[sea_orm(primary_key)]pub id: i32,pub name: String,}#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]pub enum Relation {#[sea_orm(has_many = "super::fruit::Entity")]Fruit,}impl Related<super::fruit::Entity> for Entity {fn to() -> RelationDef {Relation::Fruit.def()}}
SeaORM models 1-N and M-N relationships at the Entity level, letting you traverse many-to-many links through a junction table in a single call.
// find all modelslet cakes: Vec<cake::Model> = Cake::find().all(db).await?;// find and filterlet chocolate: Vec<cake::Model> = Cake::find().filter(cake::Column::Name.contains("chocolate")).all(db).await?;// find one modellet cheese: Option<cake::Model> = Cake::find_by_id(1).one(db).await?;let cheese: cake::Model = cheese.unwrap();// find related models (lazy)let fruits: Vec<fruit::Model> = cheese.find_related(Fruit).all(db).await?;// find related models (eager): for 1-1 relationslet cake_with_fruit: Vec<(cake::Model, Option<fruit::Model>)> =Cake::find().find_also_related(Fruit).all(db).await?;// find related models (eager): works for both 1-N and M-N relationslet cake_with_fruits: Vec<(cake::Model, Vec<fruit::Model>)> = Cake::find().find_with_related(Fruit) // for M-N relations, two joins are performed.all(db) // rows are automatically consolidated by left entity.await?;
Partial models prevent overfetching by letting you querying only the fields you need; it also makes writing deeply nested relational queries simple.
use sea_orm::DerivePartialModel;#[derive(DerivePartialModel)]#[sea_orm(entity = "cake::Entity")]struct CakeWithFruit {id: i32,name: String,#[sea_orm(nested)]fruit: Option<fruit::Model>, // this can be a regular or another partial model}let cakes: Vec<CakeWithFruit> = Cake::find().left_join(fruit::Entity) // no need to specify join condition.into_partial_model() // only the columns in the partial model will be selected.all(db).await?;
SeaORM's ActiveModel lets you work directly with Rust data structures and persist them through a simple API.
let apple = fruit::ActiveModel {name: Set("Apple".to_owned()),..Default::default() // no need to set primary key};let pear = fruit::ActiveModel {name: Set("Pear".to_owned()),..Default::default()};// insert one: Active Record stylelet apple = apple.insert(db).await?;apple.id == 1;// insert one: repository stylelet result = Fruit::insert(apple).exec(db).await?;result.last_insert_id == 1;// insert many returning last insert idlet result = Fruit::insert_many([apple, pear]).exec(db).await?;result.last_insert_id == Some(2);
You can take advantage of database specific features to perform upsert and idempotent insert.
// insert many with returning (if supported by database)let models: Vec<fruit::Model> = Fruit::insert_many([apple, pear]).exec_with_returning(db).await?;models[0]== fruit::Model {id: 1, // database assigned valuename: "Apple".to_owned(),cake_id: None,};// insert with ON CONFLICT on primary key do nothing, with MySQL specific polyfilllet result = Fruit::insert_many([apple, pear]).on_conflict_do_nothing().exec(db).await?;matches!(result, TryInsertResult::Conflicted);
ActiveModel avoids race conditions by updating only the fields you've changed, never overwriting untouched columns. You can also craft complex bulk update queries with a fluent query building API.
use fruit::Column::CakeId;use sea_orm::sea_query::{Expr, Value};let pear: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;let mut pear: fruit::ActiveModel = pear.unwrap().into();pear.name = Set("Sweet pear".to_owned()); // update value of a single field// update one: only changed columns will be updatedlet pear: fruit::Model = pear.update(db).await?;// update many: UPDATE "fruit" SET "cake_id" = "cake_id" + 2// WHERE "fruit"."name" LIKE '%Apple%'Fruit::update_many().col_expr(CakeId, Expr::col(CakeId).add(Expr::val(2))).filter(fruit::Column::Name.contains("Apple")).exec(db).await?;
You can perform "insert or update" operation with ActiveModel, making it easy to compose transactional operations.
let banana = fruit::ActiveModel {id: NotSet,name: Set("Banana".to_owned()),..Default::default()};// create, because primary key `id` is `NotSet`let mut banana = banana.save(db).await?;banana.id == Unchanged(2);banana.name = Set("Banana Mongo".to_owned());// update, because primary key `id` is presentlet banana = banana.save(db).await?;
The same ActiveModel API consistent with insert and update.
// delete one: Active Record stylelet orange: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;let orange: fruit::Model = orange.unwrap();orange.delete(db).await?;// delete one: repository stylelet orange = fruit::ActiveModel {id: Set(2),..Default::default()};fruit::Entity::delete(orange).exec(db).await?;// delete many: DELETE FROM "fruit" WHERE "fruit"."name" LIKE '%Orange%'fruit::Entity::delete_many().filter(fruit::Column::Name.contains("Orange")).exec(db).await?;
Let SeaORM handle 90% of all the transactional queries. When your query is too complex to express, SeaORM still offer convenience in writing raw SQL.
#[derive(FromQueryResult)]struct CakeWithBakery {name: String,#[sea_orm(nested)]bakery: Option<Bakery>,}#[derive(FromQueryResult)]struct Bakery {#[sea_orm(alias = "bakery_name")]name: String,}let cake_ids = [2, 3, 4]; // expanded by the `..` operator// can use many APIs with raw SQL, including nested selectlet cake: Option<CakeWithBakery> = CakeWithBakery::find_by_statement(raw_sql!(Sqlite,r#"SELECT "cake"."name", "bakery"."name" AS "bakery_name"FROM "cake"LEFT JOIN "bakery" ON "cake"."bakery_id" = "bakery"."id"WHERE "cake"."id" IN ({..cake_ids})"#)).one(db).await?;
SeaORM ➕ GraphQL = 🧭 Seaography
With Seaography, you can quickly develop and launch a fully-fledged GraphQL server!

SeaORM ➕ React = 🖥️ SeaORM Pro
With SeaORM Pro, you can easily launch an admin panel for your application, frontend development skills not required!
Express your passion for Rust
The Rustacean Sticker Pack 🦀 are made with a premium water-resistant vinyl with a unique matte finish.
All proceeds contributes directly to open-source development.
Meet Terres, our official mascot
A friend of Ferris, Terres the hermit crab is a member of the Rustacean family.
