Skip to main content

Introducing SeaORM

· 5 min read
Chris Tsang

We are pleased to introduce SeaORM 0.2.2 to the Rust community today. It's our pleasure to have received feedback and contributions from awesome people to SeaQuery and SeaORM since 0.1.0.

Rust is a wonderful language that can be used to build anything. One of the FAQs is "Are We Web Yet?", and if Rocket (or your favourite web framework) is Rust's Rail, then SeaORM is precisely Rust's ActiveRecord.

SeaORM is an async ORM built from the ground up, designed to play well with the async ecosystem, whether it's actix, async-std, tokio or any web framework built on top.

Let's have a quick tour of SeaORM.

Async

Here is how you'd execute multiple queries in parallel:

// execute multiple queries in parallel
let cakes_and_fruits: (Vec<cake::Model>, Vec<fruit::Model>) =
futures::try_join!(Cake::find().all(&db), Fruit::find().all(&db))?;

Dynamic

You can use SeaQuery to build complex queries without 'fighting the ORM':

// build subquery with ease
let cakes_with_filling: Vec<cake::Model> = cake::Entity::find()
.filter(
Condition::any().add(
cake::Column::Id.in_subquery(
Query::select()
.column(cake_filling::Column::CakeId)
.from(cake_filling::Entity)
.to_owned(),
),
),
)
.all(&db)
.await?;

More on SeaQuery

Testable

To write unit tests, you can use our mock interface:

// Setup mock connection
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results(vec![
vec![
cake::Model {
id: 1,
name: "New York Cheese".to_owned(),
},
],
])
.into_connection();

// Perform your application logic
assert_eq!(
cake::Entity::find().one(&db).await?,
Some(cake::Model {
id: 1,
name: "New York Cheese".to_owned(),
})
);

// Compare it against the expected transaction log
assert_eq!(
db.into_transaction_log(),
vec![
Transaction::from_sql_and_values(
DbBackend::Postgres,
r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
vec![1u64.into()]
),
]
);

More on testing

Service Oriented

Here is an example Rocket handler with pagination:

#[get("/?<page>&<posts_per_page>")]
async fn list(
conn: Connection<Db>,
page: Option<usize>,
per_page: Option<usize>,
) -> Template {
// Set page number and items per page
let page = page.unwrap_or(1);
let per_page = per_page.unwrap_or(10);

// Setup paginator
let paginator = Post::find()
.order_by_asc(post::Column::Id)
.paginate(&conn, per_page);
let num_pages = paginator.num_pages().await.unwrap();

// Fetch paginated posts
let posts = paginator
.fetch_page(page - 1)
.await
.expect("could not retrieve posts");

Template::render(
"index",
context! {
page: page,
per_page: per_page,
posts: posts,
num_pages: num_pages,
},
)
}

Full Rocket example

We are building more examples for other web frameworks too.

People

SeaQL is a community driven project. We welcome you to participate, contribute and together build for Rust's future.

Core Members

Chris Tsang
Billy Chan

Contributors

As a courtesy, here is the list of SeaQL's early contributors (in alphabetic order):

Ari Seyhun
Ayomide Bamidele
Ben Armstead
Bobby Ng
Daniel Lyne
Hirtol
Sylvie Rinner
Marco Napetti
Markus Merklinger
Muhannad
nitnelave
Raphaël Duchaîne
Rémi Kalbe
Sam Samai