Skip to main content

69 posts tagged with "news"

View All Tags

Getting Started with Seaography

Β· 6 min read
SeaQL Team
Chris Tsang

Seaography is a GraphQL framework for building GraphQL resolvers using SeaORM. It ships with a CLI tool that can generate ready-to-compile Rust projects from existing MySQL, Postgres and SQLite databases.

The design and implementation of Seaography can be found on our release blog post and documentation.

Extending a SeaORM project​

Since Seaography is built on top of SeaORM, you can easily build a GraphQL server from a SeaORM project.

Start by adding Seaography and GraphQL dependencies to your Cargo.toml.

[dependencies]
sea-orm = { version = "^0.9", features = [ ... ] }
+ seaography = { version = "^0.1", features = [ "with-decimal", "with-chrono" ] }
+ async-graphql = { version = "4.0.10", features = ["decimal", "chrono", "dataloader"] }
+ async-graphql-poem = { version = "4.0.10" }

Then, derive a few macros on the SeaORM entities.

use sea_orm::entity::prelude::*;

#[derive(
Clone,
Debug,
PartialEq,
DeriveEntityModel,
+ async_graphql::SimpleObject,
+ seaography::macros::Filter,
)]
+ #[graphql(complex)]
+ #[graphql(name = "FilmActor")]
#[sea_orm(table_name = "film_actor")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub actor_id: i32,
#[sea_orm(primary_key, auto_increment = false)]
pub film_id: i32,
pub last_update: DateTimeUtc,
}

#[derive(
Copy,
Clone,
Debug,
EnumIter,
DeriveRelation,
+ seaography::macros::RelationsCompact,
)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::film::Entity",
from = "Column::FilmId",
to = "super::film::Column::FilmId",
on_update = "Cascade",
on_delete = "NoAction"
)]
Film,
#[sea_orm(
belongs_to = "super::actor::Entity",
from = "Column::ActorId",
to = "super::actor::Column::ActorId",
on_update = "Cascade",
on_delete = "NoAction"
)]
Actor,
}

We also need to define QueryRoot for the GraphQL server. This define the GraphQL schema.

#[derive(Debug, seaography::macros::QueryRoot)]
#[seaography(entity = "crate::entities::actor")]
#[seaography(entity = "crate::entities::film")]
#[seaography(entity = "crate::entities::film_actor")]
pub struct QueryRoot;
use sea_orm::prelude::*;

pub mod entities;
pub mod query_root;

pub use query_root::QueryRoot;

pub struct OrmDataloader {
pub db: DatabaseConnection,
}

Finally, create an executable to drive the GraphQL server.

use async_graphql::{
dataloader::DataLoader,
http::{playground_source, GraphQLPlaygroundConfig},
EmptyMutation, EmptySubscription, Schema,
};
use async_graphql_poem::GraphQL;
use poem::{handler, listener::TcpListener, web::Html, IntoResponse, Route, Server};
use sea_orm::Database;
use seaography_example_project::*;
// ...

#[handler]
async fn graphql_playground() -> impl IntoResponse {
Html(playground_source(GraphQLPlaygroundConfig::new("/")))
}

#[tokio::main]
async fn main() {
// ...

let database = Database::connect(db_url).await.unwrap();
let orm_dataloader: DataLoader<OrmDataloader> = DataLoader::new(
OrmDataloader { db: database.clone() },
tokio::spawn,
);

let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription)
.data(database)
.data(orm_dataloader)
.finish();

let app = Route::new()
.at("/", get(graphql_playground)
.post(GraphQL::new(schema)));

Server::new(TcpListener::bind("0.0.0.0:8000"))
.run(app)
.await
.unwrap();
}

Generating a project from database​

If all you have is a database schema, good news! You can setup a GraphQL server without writing a single line of code.

Install seaography-cli, it helps you generate SeaORM entities along with a full Rust project based on a database schema.

cargo install seaography-cli

Run seaography-cli to generate code for the GraphQL server.

# The command take three arguments
seaography-cli <DATABASE_URL> <CRATE_NAME> <DESTINATION>

# MySQL
seaography-cli mysql://root:root@localhost/sakila seaography-mysql-example examples/mysql
# PostgreSQL
seaography-cli postgres://root:root@localhost/sakila seaography-postgres-example examples/postgres
# SQLite
seaography-cli sqlite://examples/sqlite/sakila.db seaography-sqlite-example examples/sqliteql

Checkout the example projects​

We have the following examples for you, alongside with the SQL scripts to initialize the database.

All examples provide a web-based GraphQL playground when running, so you can inspect the GraphQL schema and make queries. We also hosted a demo GraphQL playground in case you can't wait to play with it.

Starting the GraphQL Server​

Your GraphQL server is ready to launch! Go to the Rust project root then execute cargo run to spin it up.

$ cargo run

Playground: http://localhost:8000

Visit the GraphQL playground at http://localhost:8000

GraphQL Playground

Query Data via GraphQL​

Let say we want to get the first 3 films released on or after year 2006 sorted in ascending order of its title.

{
film(
pagination: { limit: 3, page: 0 }
filters: { releaseYear: { gte: "2006" } }
orderBy: { title: ASC }
) {
data {
filmId
title
description
releaseYear
filmActor {
actor {
actorId
firstName
lastName
}
}
}
pages
current
}
}

We got the following JSON result after running the GraphQL query.

{
"data": {
"film": {
"data": [
{
"filmId": 1,
"title": "ACADEMY DINOSAUR",
"description": "An Epic Drama of a Feminist And a Mad Scientist who must Battle a Teacher in The Canadian Rockies",
"releaseYear": "2006",
"filmActor": [
{
"actor": {
"actorId": 1,
"firstName": "PENELOPE",
"lastName": "GUINESS"
}
},
{
"actor": {
"actorId": 10,
"firstName": "CHRISTIAN",
"lastName": "GABLE"
}
},
// ...
]
},
{
"filmId": 2,
"title": "ACE GOLDFINGER",
"description": "A Astounding Epistle of a Database Administrator And a Explorer who must Find a Car in Ancient China",
"releaseYear": "2006",
"filmActor": [
// ...
]
},
// ...
],
"pages": 334,
"current": 0
}
}
}

Behind the scene, the following SQL were queried:

SELECT "film"."film_id",
"film"."title",
"film"."description",
"film"."release_year",
"film"."language_id",
"film"."original_language_id",
"film"."rental_duration",
"film"."rental_rate",
"film"."length",
"film"."replacement_cost",
"film"."rating",
"film"."special_features",
"film"."last_update"
FROM "film"
WHERE "film"."release_year" >= '2006'
ORDER BY "film"."title" ASC
LIMIT 3 OFFSET 0

SELECT "film_actor"."actor_id", "film_actor"."film_id", "film_actor"."last_update"
FROM "film_actor"
WHERE "film_actor"."film_id" IN (1, 3, 2)

SELECT "actor"."actor_id", "actor"."first_name", "actor"."last_name", "actor"."last_update"
FROM "actor"
WHERE "actor"."actor_id" IN (24, 162, 20, 160, 1, 188, 123, 30, 53, 40, 2, 64, 85, 198, 10, 19, 108, 90)

Under the hood, Seaography uses async_graphql::dataloader in querying nested objects to tackle the N+1 problem.

To learn more, checkout the Seaography Documentation.

Conclusion​

Seaography is an ergonomic library that turns SeaORM entities into GraphQL nodes. It provides a set of utilities and combined with a code generator makes GraphQL API building a breeze.

However, Seaography is still a new-born. Like all other open-source projects developed by passionate Rust developers, you can contribute to it if you also find the concept interesting. With its addition to the SeaQL ecosystem, we are one step closer to the vision of Rust being the best tool for data engineering.

People​

Seaography is created by:

Panagiotis Karatakis

Summer of Code Contributor; developer of Seaography

Chris Tsang

Summer of Code Mentor; lead developer of SeaQL

Billy Chan

Summer of Code Mentor; core member of SeaQL

Introducing Seaography 🧭

Β· 5 min read
SeaQL Team
Chris Tsang

What a fruitful Summer of Code! Today, we are excited to introduce Seaography to the SeaQL community. Seaography is a GraphQL framework for building GraphQL resolvers using SeaORM. It ships with a CLI tool that can generate ready-to-compile Rust projects from existing MySQL, Postgres and SQLite databases.

Motivation​

We observed that other ecosystems have similar tools such as PostGraphile and Hasura allowing users to query a database via GraphQL with minimal effort upfront. We decided to bring that seamless experience to the Rust ecosystem.

For existing SeaORM users, adding a GraphQL API is straight forward. Start by adding seaography and async-graphql dependencies to your crate. Then, deriving a few extra derive macros to the SeaORM entities. Finally, spin up a GraphQL server to serve queries!

If you are new to SeaORM, no worries, we have your back. You only need to provide a database connection, and seaography-cli will generate the SeaORM entities together with a complete Rust project!

Design​

We considered two approaches in our initial discussion: 1) blackbox query engine 2) code generator. The drawback with a blackbox query engine is it's difficult to customize or extend its behaviour, making it difficult to develop and operate in the long run. We opted the code generator approach, giving users full control and endless possibilities with the versatile async Rust ecosystem.

This project is separated into the following crates:

  • seaography: The facade crate; exporting macros, structures and helper functions to turn SeaORM entities into GraphQL nodes.

  • seaography-cli: The CLI tool; it generates SeaORM entities along with a full Rust project based on a user-provided database.

  • seaography-discoverer: A helper crate used by the CLI tool to discover the database schema and transform into a generic format.

  • seaography-generator: A helper crate used by the CLI tool to consume the database schema and generate a full Rust project.

  • seaography-derive: A set of procedural macros to derive types and trait implementations on SeaORM entities, turning them into GraphQL nodes.

Features​

  • Relational query (1-to-1, 1-to-N)
  • Pagination on query's root entity
  • Filter with operators (e.g. gt, lt, eq)
  • Order by any column

Getting Started​

To quick start, we have the following examples for you, alongside with the SQL scripts to initialize the database.

All examples provide a web-based GraphQL playground when running, so you can inspect the GraphQL schema and make queries. We also hosted a demo GraphQL playground in case you can't wait to play with it.

For more documentation, visit www.sea-ql.org/Seaography.

What's Next?​

This project passed the first milestone shipping the essential features, but it still has a long way to go. The next milestone would be:

  • Query enhancements
    • Filter related queries
    • Filter based on related queries properties
    • Paginate related queries
    • Order by related queries
  • Cursor based pagination
  • Single entity query
  • Mutations
    • Insert single entity
    • Insert batch entities
    • Update single entity
    • Update batch entities using filter
    • Delete single entity
    • Delete batch entities

Conclusion​

Seaography is an ergonomic library that turns SeaORM entities into GraphQL nodes. It provides a set of utilities and combined with a code generator makes GraphQL API building a breeze.

However, Seaography is still a new-born. Like all other open-source projects developed by passionate Rust developers, you can contribute to it if you also find the concept interesting. With its addition to the SeaQL ecosystem, we are one step closer to the vision of Rust being the best tool for data engineering.

People​

Seaography is created by:

Panagiotis Karatakis

Summer of Code Contributor; developer of Seaography

Chris Tsang

Summer of Code Mentor; lead developer of SeaQL

Billy Chan

Summer of Code Mentor; core member of SeaQL

Celebrating 3,000+ GitHub Stars πŸŽ‰

Β· 8 min read
SeaQL Team
Chris Tsang

We are celebrating the milestone of reaching 3,000 GitHub stars across all SeaQL repositories!

This wouldn't have happened without your support and contribution, so we want to thank the community for being with us along the way.

The Journey​

SeaQL.org was founded back in 2020. We devoted ourselves into developing open source libraries that help Rust developers to build data intensive applications. In the past two years, we published and maintained four open source libraries: SeaQuery, SeaSchema, SeaORM and StarfishQL. Each library is designed to fill a niche in the Rust ecosystem, and they are made to play well with other Rust libraries.

2020​

  • Oct 2020: SeaQL founded
  • Dec 2020: SeaQuery first released

2021​

  • Apr 2021: SeaSchema first released
  • Aug 2021: SeaORM first released
  • Nov 2021: SeaORM reached 0.4.0
  • Dec 2021: SeaQuery reached 0.20.0
  • Dec 2021: SeaSchema reached 0.4.0

2022​

  • Apr 2022: SeaQL selected as a Google Summer of Code 2022 mentor organization
  • Apr 2022: StarfishQL first released
  • Jul 2022: SeaQuery reached 0.26.2
  • Jul 2022: SeaSchema reached 0.9.3
  • Jul 2022: SeaORM reached 0.9.1
  • Aug 2022: SeaQL reached 3,000+ GitHub stars

Where're We Now?​

We're pleased by the adoption by the Rust community. We couldn't make it this far without your feedback and contributions.

4 πŸ“¦

Open source projects

5 🏬

Startups using SeaQL

1,972 🎈

Dependent projects

131 πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦

Contributors

1,061 βœ…

Merged PRs & resolved issues

3,158 ⭐

GitHub stars

432 πŸ—£οΈ

Discord members

87,937 ⌨️

Lines of Rust

667,769 πŸ’Ώ

Downloads on crates.io

* as of Aug 12

Core Members​

Our team has grown from two people initially into four. We always welcome passionate engineers to join us!

Chris Tsang

Founder. Led the initial development and maintaining the projects.

Billy Chan

Founding member. Contributed many features and bug fixes. Keeps the community alive.

Ivan Krivosheev

Joined in 2022. Contributed many features and bug fixes, most notably to SeaQuery.

Sanford Pun

Developed StarfishQL and wrote SeaORM's tutorial.

Special Thanks​

Marco Napetti

Contributed transaction, streaming and tracing API to SeaORM.

nitnelave

Contributed binder crate and other improvements to SeaQuery.

Sam Samai

Developed SeaORM's test suite and demo schema.

Daniel Lyne

Developed SeaSchema's Postgres implementation.

Charles Chege

Developed SeaSchema's SQLite implementation.

Sponsors​

If you are feeling generous, a small donation will be greatly appreciated.

A big shout out to our sponsors πŸ˜‡:

Γ‰mile Fugulin

Dean Sheather

Shane Sveller

Sakti Dwi Cahyono

Unnamed Sponsor

Unnamed Sponsor

Contributors​

Many features and enhancements are actually proposed and implemented by the community. We want to take this chance to thank all our contributors!

What's Next?​

We have two ongoing Summer of Code 2022 projects to enrich the SeaQL ecosystem, planning to be released later this year. In the meantime, we're focusing on improving existing SeaQL libraries until reaching version 1.0, we'd love to hear comments and feedback from the community.

If you like what we do, consider starring, commenting, sharing, contributing and together building for Rust's future!

What's new in SeaQuery 0.26.0

Β· 4 min read
SeaQL Team
Ivan Krivosheev

πŸŽ‰ We are pleased to release SeaQuery 0.26.0! Here are some feature highlights 🌟:

Dependency Upgrades​

[#356] We have upgraded a few major dependencies:

Note that you might need to upgrade the corresponding dependency on your application as well.

VALUES lists​

[#351] Add support for VALUES lists

// SELECT * FROM (VALUES (1, 'hello'), (2, 'world')) AS "x"
let query = SelectStatement::new()
.expr(Expr::asterisk())
.from_values(vec![(1i32, "hello"), (2, "world")], Alias::new("x"))
.to_owned();

assert_eq!(
query.to_string(PostgresQueryBuilder),
r#"SELECT * FROM (VALUES (1, 'hello'), (2, 'world')) AS "x""#
);

Introduce sea-query-binder​

[#273] Native support SQLx without marcos

use sea_query_binder::SqlxBinder;

// Create SeaQuery query with prepare SQLx
let (sql, values) = Query::select()
.columns([
Character::Id,
Character::Uuid,
Character::Character,
Character::FontSize,
Character::Meta,
Character::Decimal,
Character::BigDecimal,
Character::Created,
Character::Inet,
Character::MacAddress,
])
.from(Character::Table)
.order_by(Character::Id, Order::Desc)
.build_sqlx(PostgresQueryBuilder);

// Execute query
let rows = sqlx::query_as_with::<_, CharacterStructChrono, _>(&sql, values)
.fetch_all(&mut pool)
.await?;

// Print rows
for row in rows.iter() {
println!("{:?}", row);
}

CASE WHEN statement support​

[#304] Add support for CASE WHEN statement

let query = Query::select()
.expr_as(
CaseStatement::new()
.case(Expr::tbl(Glyph::Table, Glyph::Aspect).is_in(vec![2, 4]), Expr::val(true))
.finally(Expr::val(false)),
Alias::new("is_even")
)
.from(Glyph::Table)
.to_owned();

assert_eq!(
query.to_string(PostgresQueryBuilder),
r#"SELECT (CASE WHEN ("glyph"."aspect" IN (2, 4)) THEN TRUE ELSE FALSE END) AS "is_even" FROM "glyph""#
);

Add support for Ip(4,6)Network and MacAddress​

[#309] Add support for Network types in PostgreSQL backend

Introduce sea-query-attr​

[#296] Proc-macro for deriving Iden enum from struct

use sea_query::gen_type_def;

#[gen_type_def]
pub struct Hello {
pub name: String
}

println!("{:?}", HelloTypeDef::Name);

Add ability to alter foreign keys​

[#299] Add support for ALTER foreign Keys

let foreign_key_char = TableForeignKey::new()
.name("FK_character_glyph")
.from_tbl(Char::Table)
.from_col(Char::FontId)
.from_col(Char::Id)
.to_tbl(Glyph::Table)
.to_col(Char::FontId)
.to_col(Char::Id)
.to_owned();

let table = Table::alter()
.table(Character::Table)
.add_foreign_key(&foreign_key_char)
.to_owned();

assert_eq!(
table.to_string(PostgresQueryBuilder),
vec![
r#"ALTER TABLE "character""#,
r#"ADD CONSTRAINT "FK_character_glyph""#,
r#"FOREIGN KEY ("font_id", "id") REFERENCES "glyph" ("font_id", "id")"#,
r#"ON DELETE CASCADE ON UPDATE CASCADE,"#,
]
.join(" ")
);

Select DISTINCT ON​

[#250]

let query = Query::select()
.from(Char::Table)
.distinct_on(vec![Char::Character])
.column(Char::Character)
.column(Char::SizeW)
.column(Char::SizeH)
.to_owned();

assert_eq!(
query.to_string(PostgresQueryBuilder),
r#"SELECT DISTINCT ON ("character") "character", "size_w", "size_h" FROM "character""#
);

Miscellaneous Enhancements​

  • [#353] Support LIKE ... ESCAPE ... expression
  • [#306] Move escape and unescape string to backend
  • [#365] Add method to make a column nullable
  • [#348] Add is & is_not to Expr
  • [#349] Add CURRENT_TIMESTAMP function
  • [#345] Add in_tuple method to Expr
  • [#266] Insert Default
  • [#324] Make sea-query-driver an optional dependency
  • [#334] Add ABS function
  • [#332] Support IF NOT EXISTS when create index
  • [#314] Support different blob types in MySQL
  • [#331] Add VarBinary column type
  • [#335] RETURNING expression supporting SimpleExpr

Integration Examples​

SeaQuery plays well with the other crates in the rust ecosystem.

Community​

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

Engineering at SeaQL.org

Β· 5 min read
Chris Tsang
Lead Developer

It's hard to pin down the exact date, but I think SeaQL.org was setup in July 2020, a little over a year ago. Over the course of the year, SeaORM went from 0.1 to 0.9 and the number of users kept growing. I would like to outline our engineering process in this blog post, and perhaps it can serve as a reference or guidance to prospective contributors and the future maintainer of this project.

In the open source world, the Benevolent Dictator for Life (BDL) model underpins a number of successful open source projects. That's not me! As a maintainer, I believe in an open, bottom-up, iterative and progressive approach. Let me explain each of these words and what they mean to me.

Open​

Open as in source availability, but also engineering. We always welcome new contributors! We'd openly discuss ideas and designs. I would often explain why a decision was made in the first place for various things. The project is structured not as a monorepo, but several interdependent repos. This reduces the friction for new contributors, because they can have a smaller field of vision to focus on solving one particular problem at hand.

Bottom-up​

We rely on users to file feature requests, bug reports and of course pull requests to drive the project forward. The great thing is, for every feature / bug fix, there is a use case for it and a confirmation from a real user that it works and is reasonable. As a maintainer, I could not have first hand experience for all features and so could not understand some of the pain points.

Iterative​

Open source software is imperfect, impermanent and incomplete. While I do have a grand vision in mind, we do not try rushing it all the way in one charge, nor keeping a project secret until it is 'complete'. Good old 'release early, release often' - we would release an initial working version of a tool, gather user feedback and improve upon it, often reimplementing a few things and break a few others - which brings us to the next point.

Progressive​

Favour progression. Always look forward and leave legacy behind. It does not mean that we would arbitrary break things, but when a decision is made, we'd always imagine how the software should be without historic context. We'd provide migrate paths and encourage users to move forward with us. After all, Rust is a young and evolving language! You may or may not know that async was just stabilized in 2020.

Enough said for the philosophy, let's now talk about the actual engineering process.

1. Idea & Design​

We first have some vague idea on what problem we want to tackle. As we put in more details to the use case, we can define the problem and brainstorm solutions. Then we look for workable ways to implement that in Rust.

2. Implementation​

An initial proof of concept is appreciated. We iterate on the implementation to reduce the impact and improve the maintainability.

3. Testing​

We rely on automated tests. Every feature should come with corresponding tests, and a release is good if and only if all tests are green. Which means for features not covered by our test suite, it is an uncertainty to when we would break them. So if certain undocumented feature is important to you, we encourage you to add that to our test suite.

4. Documentation​

Coding is not complete without documentation. Rust doc tests kill two birds with one stone and so is greatly appreciated. For SeaORM we have separate documentation repository and tutorial repository. It takes a lot of effort to maintain those to be up to date, and right now it's mostly done by our core contributors.

5. Release​

We run on a release train model, although the frequency varies. The ethos is to have small number breaking changes often. At one point, SeaQuery has a new release every week. SeaORM runs on monthly, although it more or less relaxes to bimonthly now. At any time, we maintain two branches, the latest release and master. PRs are always merged into master, and if it is non-breaking (and worthy) I would backport it to the release branch and make a minor release. At the end, I want to maintain momentum and move forward together with the community. Users can have a rough expectation on when merges will be released. And there are just lots of change we cannot avoid a breaking release as of the current state of the Rust ecosystem. Users are advised to upgrade regularly, and we ship along many small improvements to encourage that.

Conclusion​

Open source software is a collaborative effort and thank you all who participated! Also a big thanks to SeaQL's core contributors who made wonders. If you have not already, I invite you to star all our repositories. If you want to support us materially, a small donation would make a big difference. SeaQL the organization is still in its infancy, and your support is vital to SeaQL's longevity and the prospect of the Rust community.

What's new in SeaORM 0.9.0

Β· 16 min read
SeaQL Team
Chris Tsang

πŸŽ‰ We are pleased to release SeaORM 0.9.0 today! Here are some feature highlights 🌟:

Dependency Upgrades​

[#834] We have upgraded a few major dependencies:

Note that you might need to upgrade the corresponding dependency on your application as well.

Proposed by:

Rob Gilson

boraarslan

Contributed by:

Billy Chan

Cursor Pagination​

[#822] Paginate models based on column(s) such as the primary key.

// Create a cursor that order by `cake`.`id`
let mut cursor = cake::Entity::find().cursor_by(cake::Column::Id);

// Filter paginated result by `cake`.`id` > 1 AND `cake`.`id` < 100
cursor.after(1).before(100);

// Get first 10 rows (order by `cake`.`id` ASC)
let rows: Vec<cake::Model> = cursor.first(10).all(db).await?;

// Get last 10 rows (order by `cake`.`id` DESC but rows are returned in ascending order)
let rows: Vec<cake::Model> = cursor.last(10).all(db).await?;

Proposed by:

Lucas Berezy

Contributed by:

Γ‰mile Fugulin

Billy Chan

Insert On Conflict​

[#791] Insert an active model with on conflict behaviour.

let orange = cake::ActiveModel {
id: ActiveValue::set(2),
name: ActiveValue::set("Orange".to_owned()),
};

// On conflict do nothing:
// - INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO NOTHING
cake::Entity::insert(orange.clone())
.on_conflict(
sea_query::OnConflict::column(cake::Column::Name)
.do_nothing()
.to_owned()
)
.exec(db)
.await?;

// On conflict do update:
// - INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO UPDATE SET "name" = "excluded"."name"
cake::Entity::insert(orange)
.on_conflict(
sea_query::OnConflict::column(cake::Column::Name)
.update_column(cake::Column::Name)
.to_owned()
)
.exec(db)
.await?;

Proposed by:

baoyachi. Aka Rust Hairy crabs

Contributed by:

liberwang1013

Join Table with Custom Conditions and Table Alias​

[#793, #852] Click Custom Join Conditions and Custom Joins to learn more.

assert_eq!(
cake::Entity::find()
.column_as(
Expr::tbl(Alias::new("fruit_alias"), fruit::Column::Name).into_simple_expr(),
"fruit_name"
)
.join_as(
JoinType::LeftJoin,
cake::Relation::Fruit
.def()
.on_condition(|_left, right| {
Expr::tbl(right, fruit::Column::Name)
.like("%tropical%")
.into_condition()
}),
Alias::new("fruit_alias")
)
.build(DbBackend::MySql)
.to_string(),
[
"SELECT `cake`.`id`, `cake`.`name`, `fruit_alias`.`name` AS `fruit_name` FROM `cake`",
"LEFT JOIN `fruit` AS `fruit_alias` ON `cake`.`id` = `fruit_alias`.`cake_id` AND `fruit_alias`.`name` LIKE '%tropical%'",
]
.join(" ")
);

Proposed by:

Chris Tsang

Tuetuopay

LoΓ―c

Contributed by:

Billy Chan

Matt

liberwang1013

(de)serialize Custom JSON Type​

[#794] JSON stored in the database could be deserialized into custom struct in Rust.

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "json_struct")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
// JSON column defined in `serde_json::Value`
pub json: Json,
// JSON column defined in custom struct
pub json_value: KeyValue,
pub json_value_opt: Option<KeyValue>,
}

// The custom struct must derive `FromJsonQueryResult`, `Serialize` and `Deserialize`
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, FromJsonQueryResult)]
pub struct KeyValue {
pub id: i32,
pub name: String,
pub price: f32,
pub notes: Option<String>,
}

Proposed by:

Mara Schulke

Chris Tsang

Contributed by:

Billy Chan

Derived Migration Name​

[#736] Introduce DeriveMigrationName procedural macros to infer migration name from the file name.

use sea_orm_migration::prelude::*;

// Used to be...
pub struct Migration;

impl MigrationName for Migration {
fn name(&self) -> &str {
"m20220120_000001_create_post_table"
}
}

// Now... derive `DeriveMigrationName`,
// no longer have to specify the migration name explicitly
#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table( ... )
.await
}

async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table( ... )
.await
}
}

Proposed by:

Chris Tsang

Contributed by:

smonv

Lukas Potthast

Billy Chan

SeaORM CLI Improvements​

  • [#735] Improve logging of generate entity command
  • [#588] Generate enum with numeric like variants
  • [#755] Allow old pending migration to be applied
  • [#837] Skip generating entity for ignored tables
  • [#724] Generate code for time crate
  • [#850] Add various blob column types
  • [#422] Generate entity files with Postgres's schema name
  • [#851] Skip checking connection string for credentials

Proposed & Contributed by:

ttys3

kyoto7250

yb3616

Γ‰mile Fugulin

Bastian

Nahua

Mike

Frank Horvath

Maikel Wever

Miscellaneous Enhancements​

  • [#800] Added sqlx_logging_level to ConnectOptions
  • [#768] Added num_items_and_pages to Paginator
  • [#849] Added TryFromU64 for time
  • [#853] Include column name in TryGetError::Null
  • [#778] Refactor stream metrics

Proposed & Contributed by:

SandaruKasa

Eric

Γ‰mile Fugulin

Renato Dinhani

kyoto7250

Marco Napetti

Integration Examples​

SeaORM plays well with the other crates in the async ecosystem. We maintain an array of example projects for building REST, GraphQL and gRPC services. More examples wanted!

Our GitHub Sponsor profile is up! If you feel generous, a small donation will be greatly appreciated.

A big shout out to our sponsors πŸ˜‡:

Γ‰mile Fugulin

Dean Sheather

Shane Sveller

Sakti Dwi Cahyono

Unnamed Sponsor

Unnamed Sponsor

Community​

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

Here is the roadmap for SeaORM 0.10.x.

Welcome Summer of Code 2022 Contributors

Β· 5 min read
SeaQL Team
Chris Tsang

We are thrilled to announce that we will bring in four contributors this summer! Two of them are sponsored by Google while two of them are sponsored by SeaQL.

A GraphQL Framework on Top of SeaORM​

Panagiotis Karatakis

I'm Panagiotis, I live in Athens Greece and currently I pursue my second bachelors on economic sciences. My first bachelors was on computer science and I've a great passion on studying and implementing enterprise software solutions. I know Rust the last year and I used it almost daily for a small startup project that me and my friends build for a startup competition.

I'll be working on creating a CLI tool that will explore a database schema and then generate a ready to build async-graphql API. The tool will allow quick integration with the SeaQL and Rust ecosystems as well as GraphQL. To be more specific, for database exploring I'll use sea-schema and sea-orm-codegen for entity generation, my job is to glue those together with async-graphql library. You can read more here.

SQL Interpreter for Mock Testing​

Samyak Sarnayak

I'm Samyak Sarnayak, a final year Computer Science student from Bangalore, India. I started learning Rust around 6-7 months ago and it feels like I have found the perfect language for me :D. It does not have a runtime, has a great type system, really good compiler errors, good tooling, some functional programming patterns and metaprogramming. You can find more about me on my GitHub profile.

I'll be working on a new SQL interpreter for mock testing. This will be built specifically for testing and so the emphasis will be on correctness - it can be slow but the operations must always be correct. I'm hoping to build a working version of this and integrate it into the existing tests of SeaORM. Here is the discussion for this project.

Support TiDB in the SeaQL Ecosystem​

Edit: This project was canceled.

Query Linter for SeaORM​

Edit: This project was canceled.

Mentors​

Chris Tsang

I am a strong believer in open source. I started my GitHub journey 10 years ago, when I published my first programming library. I had been looking for a programming language with speed, ergonomic and expressiveness. Until I found Rust.

Seeing a niche and demand for data engineering tools in the Rust ecosystem, I founded SeaQL in 2020 and have been leading the development and maintaining the libraries since then.


Billy Chan

Hey, this is Billy from Hong Kong. I've been using open-source libraries ever since I started coding but it's until 2020, I dedicated myself to be a Rust open-source developer.

I was also a full-stack developer specialized in formulating requirement specifications for user interfaces and database structures, implementing and testing both frontend and backend from ground up, finally releasing the MVP for production and maintaining it for years to come.

I enjoy working with Rustaceans across the globe, building a better and sustainable ecosystem for Rust community. If you like what we do, consider starring, commenting, sharing and contributing, it would be much appreciated.


Sanford Pun

I'm Sanford, an enthusiastic software engineer who enjoys problem-solving! I've worked on Rust for a couple of years now. During my early days with Rust, I focused more on the field of graphics/image processing, where I fell in love with what the language has to offer! This year, I've been exploring data engineering in the StarfishQL project.

A toast to the endless potential of Rust!

Community​

If you are interested in the projects and want to share your thoughts, please star and watch the SeaQL/summer-of-code repository on GitHub and join us on our Discord server!

What's new in SeaORM 0.8.0

Β· 8 min read
SeaQL Team
Chris Tsang

πŸŽ‰ We are pleased to release SeaORM 0.8.0 today! Here are some feature highlights 🌟:

Migration Utilities Moved to sea-orm-migration crate​

[#666] Utilities of SeaORM migration have been moved from sea-schema to sea-orm-migration crate. Users are advised to upgrade from older versions with the following steps:

  1. Bump sea-orm version to 0.8.0.
  2. Replace sea-schema dependency with sea-orm-migration in your migration crate.
    [dependencies]
    - sea-schema = { version = "^0.7.0", ... }
    + sea-orm-migration = { version = "^0.8.0" }
  3. Find and replace use sea_schema::migration:: with use sea_orm_migration:: in your migration crate.
    - use sea_schema::migration::prelude::*;
    + use sea_orm_migration::prelude::*;

    - use sea_schema::migration::*;
    + use sea_orm_migration::*;

Designed by:



Chris Tsang

Contributed by:



Billy Chan

Generating New Migration​

[#656] You can create a new migration with the migrate generate subcommand. This simplifies the migration process, as new migrations no longer need to be added manually.

# A migration file `MIGRATION_DIR/src/mYYYYMMDD_HHMMSS_create_product_table.rs` will be created.
# And, the migration file will be imported and included in the migrator located at `MIGRATION_DIR/src/lib.rs`.
sea-orm-cli migrate generate create_product_table

Proposed & Contributed by:



Viktor Bahr

Inserting One with Default​

[#589] Insert a row populate with default values. Note that the target table should have default values defined for all of its columns.

let pear = fruit::ActiveModel {
..Default::default() // all attributes are `NotSet`
};

// The SQL statement:
// - MySQL: INSERT INTO `fruit` VALUES ()
// - SQLite: INSERT INTO "fruit" DEFAULT VALUES
// - PostgreSQL: INSERT INTO "fruit" VALUES (DEFAULT) RETURNING "id", "name", "cake_id"
let pear: fruit::Model = pear.insert(db).await?;

Proposed by:



Crypto-Virus

Contributed by:



Billy Chan

Checking if an ActiveModel is changed​

[#683] You can check whether any field in an ActiveModel is Set with the help of the is_changed method.

let mut fruit: fruit::ActiveModel = Default::default();
assert!(!fruit.is_changed());

fruit.set(fruit::Column::Name, "apple".into());
assert!(fruit.is_changed());

Proposed by:



Karol Fuksiewicz

Contributed by:



Kirawi

Minor Improvements​

  • [#670] Add max_connections option to sea-orm-cli generate entity subcommand
  • [#677] Derive Eq and Clone for DbErr

Proposed & Contributed by:



benluelo


Sebastien Guillemot

Integration Examples​

SeaORM plays well with the other crates in the async ecosystem. It can be integrated easily with common RESTful frameworks and also gRPC frameworks; check out our new Tonic example to see how it works. More examples wanted!

Who's using SeaORM?​

The following products are powered by SeaORM:



A lightweight web security auditing toolkit

The enterprise ready webhooks service

A personal search engine

SeaORM is the foundation of StarfishQL, an experimental graph database and query engine.

For more projects, see Built with SeaORM.

Our GitHub Sponsor profile is up! If you feel generous, a small donation will be greatly appreciated.

A big shout out to our sponsors πŸ˜‡:

Γ‰mile Fugulin

Zachary Vander Velden

Dean Sheather

Shane Sveller

Sakti Dwi Cahyono

Unnamed Sponsor

Community​

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

Here is the roadmap for SeaORM 0.9.x.

GSoC 2022​

We are super excited to be selected as a Google Summer of Code 2022 mentor organization. The application is now closed, but the program is about to start! If you have thoughts over how we are going to implement the project ideas, feel free to participate in the discussion.

SeaORM FAQ.01

Β· 2 min read
Chris Tsang
SeaQL Team

FAQ.01 Why SeaORM does not nest objects for parent-child relation?​

let cake_with_fruits: Vec<(cake::Model, Vec<fruit::Model>)> =
Cake::find().find_with_related(Fruit).all(db).await?;

Consider the above API, Cake and Fruit are two separate models.

If you come from a dynamic language, you'd probably used to:

struct Cake {
id: u64,
fruit: Fruit,
..
}

It's so convenient that you can simply:

let cake = Cake::find().one(db).await?;
println!("Fruit = {}", cake.fruit.name);

Sweet right? Okay so, the problem with this pattern is that it does not fit well with Rust.

Let's look at this playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=6fb0a981189ace081fbb2aa04f50146b

struct Parent {
a: u64,
child: Option<Child>,
}

struct ParentWithBox {
a: u64,
child: Option<Box<Child>>,
}

struct Child {
a: u64,
b: u64,
c: u64,
d: u64,
}

fn main() {
dbg!(std::mem::size_of::<Parent>());
dbg!(std::mem::size_of::<ParentWithBox>());
dbg!(std::mem::size_of::<Child>());
}

What's the output you guess?

[src/main.rs:21] std::mem::size_of::<Parent>() = 48
[src/main.rs:22] std::mem::size_of::<ParentWithBox>() = 16
[src/main.rs:23] std::mem::size_of::<Child>() = 32

In dynamic languages, objects are always held by pointers, and that maps to a Box in Rust. In Rust, we don't put objects in Boxes by default, because it forces the object to be allocated on the heap. And that is an extra cost! Because objects are always first constructed on the stack and then being copied over to the heap.

Ref:

  1. https://users.rust-lang.org/t/how-to-create-large-objects-directly-in-heap/26405
  2. https://github.com/PoignardAzur/placement-by-return/blob/placement-by-return/text/0000-placement-by-return.md

We face the dilemma where we either put the object on the stack and waste some space (it takes up 48 bytes no matter child is None or not) or put the object in a box and waste some cycles.

If you are new to Rust, all these might be unfamiliar, but a Rust programmer has to consciously make decisions over memory management, and we don't want to make decisions on behalf of our users.

That said, there were proposals to add API with this style to SeaORM, and we might implement that in the future. Hopefully this would shed some light on the matter meanwhile.

Introducing StarfishQL

Β· 12 min read
SeaQL Team
Chris Tsang

We are pleased to introduce StarfishQL to the Rust community today. StarfishQL is a graph database and query engine to enable graph analysis and visualization on the web. It is an experimental project, with its primary purpose to explore the dependency network of Rust crates published on crates.io.

Motivation​

StarfishQL is a framework for providing a graph database and a graph query engine that interacts with it.

A concrete example (Freeport) involving the graph of crate dependency on crates.io is used for illustration. With this example, you can see StarfishQL in action.

At the end of the day, we're interested in performing graph analysis, that is to extract meaningful information out of plain graph data. To achieve that, we believe that visualization is a crucial aid.

StarfishQL's query engine is designed to be able to incorporate different forms of visualization by using a flexible query language. However, the development of the project has been centred around the following, as showcased in our demo apps.

Traverse the dependency graph in the normal direction starting from the N most connected nodes.

Traverse the dependency tree in both forward and reverse directions starting from a particular node.

Design​

In general, a query engine takes input queries written in a specific query language (e.g. SQL statements), performs the necessary operations in the database, and then outputs the data of interest to the user application. You may also view a query engine as an abstraction layer such that the user can design queries simply in the supported query language and let the query engine do the rest.

In the case of a graph query engine, the output data is a graph (wiki).

Graph query engine overview

In the case of StarfishQL, the query language is a custom language we defined in the JSON format, which enables the engine to be highly accessible and portable.

Implementation​

In the example of Freeport, StarfishQL consists of the following three components.

Graph Query Engine​

As a core component of StarfishQL, the graph query engine is a Rust backend application powered by the Rocket web framework and the SeaQL ecosystem.

The engine listens at the following endpoints for the corresponding operation:

You could also invoke the endpoints above programmatically.

Graph data are stored in a relational database:

  • Metadata - Definition of each entity and relation, e.g. attributes of crates and dependency
  • Node Data - An instance of an entity, e.g. crate name and version number
  • Edge Data - An instance of a relation, e.g. one crate depends on another

crates.io Crawler​

To obtain the crate data to insert into the database, we used a fast, non-disruptive crawler on a local clone of the public index repo of crates.io.

Graph Visualization​

We used d3.js to create force-directed graphs to display the results. The two colourful graphs above are such products.

Findings​

Here are some interesting findings we made during the process.

Top-10 Dependencies

List of top 10 crates order by different decay modes.

Decay Mode: Immediate / Simple Connectivity
crateconnectivity
serde17,441
serde_json10,528
log9,220
clap6,323
thiserror5,547
rand5,340
futures5,263
lazy_static5,211
tokio5,168
chrono4,794
Decay Mode: Medium (.5) / Complex Connectivity
crateconnectivity
quote4,126
syn4,069
pure-rust-locales4,067
reqwest3,950
proc-macro23,743
num_threads3,555
value-bag3,506
futures-macro3,455
time-macros3,450
thiserror-impl3,416
Decay Mode: None / Compound Connectivity
crateconnectivity
unicode-xid54,982
proc-macro254,949
quote54,910
syn54,744
rustc-std-workspace-core51,650
libc51,645
serde_derive51,056
serde51,054
jobserver50,567
cc50,566

If we look at Decay Mode: Immediate, where the connectivity is simply the number of immediate dependants, we can see that serde and serde_json are at the top. I guess that supports our decision of defining the query language in JSON.

Decay Mode: None tells another interesting story: when the connectivity is the entire tree of dependants, we are looking at the really core crates that are nested somewhere deeply inside the most crates. In other words, these are the ones that are built along with the most crates. Under this setting, the utility crates that interacts with the low-level, more fundamental aspects of Rust are ranked higher,like quote with syntax trees, proc-macro2 with procedural macros, and unicode-xid with Unicode checking.

Number of crates without Dependencies

19,369 out of 79,972 crates, or 24% of the crates, do not depend on any crates.

e.g.Β  a,Β  a-,Β  a0,Β  ...,Β  zyx_test,Β  zz-buffer,Β  z_table

In other words, about 76% of the crates are standing on the shoulders of giants! πŸ’ͺ

Number of crates without Dependants

53,910 out of 79,972 crates, or 67% of the crates, have no dependants, i.e. no other crates depend on them.

e.g.Β  a,Β  a-,Β  a-bot,Β  ...,Β  zzp-tools,Β  zzz,Β  z_table

We imagine many of those crates are binaries/executables, if only we could figure out a way to check that... πŸ€”

As of March 30, 2022

Conclusion​

StarfishQL allows flexible and portable definition, manipulation, retrieval, and visualization of graph data.

The graph query engine built in Rust provides a nice interface for any web applications to access data in the relational graph database with stable performance and memory safety.

Admittedly, StarfishQL is still in its infancy, so every detail in the design and implementation is subject to change. Fortunately, the good thing about this is, like all other open-source projects developed by brilliant Rust developers, you can contribute to it if you also find the concept interesting. With its addition to the SeaQL ecosystem, together we are one step closer to the vision of Rust for data engineering.

People​

StarfishQL is created by the following SeaQL team members:

Chris Tsang

Billy Chan

Sanford Pun

Contributing​

We are super excited to be selected as a Google Summer of Code 2022 mentor organization!

StarfishQL is one of the GSoC project ideas that opens for development proposals. Join us on GSoC 2022 by following the instructions on GSoC Contributing Guide.

What's new in SeaORM 0.7.0

Β· 8 min read
SeaQL Team
Chris Tsang

πŸŽ‰ We are pleased to release SeaORM 0.7.0 today! Here are some feature highlights 🌟:

Update ActiveModel by JSON​

[#492] If you want to save user input into the database you can easily convert JSON value into ActiveModel.

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "fruit")]
pub struct Model {
#[sea_orm(primary_key)]
#[serde(skip_deserializing)] // Skip deserializing
pub id: i32,
pub name: String,
pub cake_id: Option<i32>,
}

Set the attributes in ActiveModel with set_from_json method.

// A ActiveModel with primary key set
let mut fruit = fruit::ActiveModel {
id: ActiveValue::Set(1),
name: ActiveValue::NotSet,
cake_id: ActiveValue::NotSet,
};

// Note that this method will not alter the primary key values in ActiveModel
fruit.set_from_json(json!({
"id": 8,
"name": "Apple",
"cake_id": 1,
}))?;

assert_eq!(
fruit,
fruit::ActiveModel {
id: ActiveValue::Set(1),
name: ActiveValue::Set("Apple".to_owned()),
cake_id: ActiveValue::Set(Some(1)),
}
);

Create a new ActiveModel from JSON value with the from_json method.

let fruit = fruit::ActiveModel::from_json(json!({
"name": "Apple",
}))?;

assert_eq!(
fruit,
fruit::ActiveModel {
id: ActiveValue::NotSet,
name: ActiveValue::Set("Apple".to_owned()),
cake_id: ActiveValue::NotSet,
}
);

Proposed by:



qltk

Contributed by:



Billy Chan

Support time crate in Model​

[#602] You can define datetime column in Model with time crate. You can migrate your Model originally defined in chrono to time crate.

Model defined in chrono crate.

use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "transaction_log")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub date: Date, // chrono::NaiveDate
pub time: Time, // chrono::NaiveTime
pub date_time: DateTime, // chrono::NaiveDateTime
pub date_time_tz: DateTimeWithTimeZone, // chrono::DateTime<chrono::FixedOffset>
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}

Model defined in time crate.

use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "transaction_log")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub date: TimeDate, // time::Date
pub time: TimeTime, // time::Time
pub date_time: TimeDateTime, // time::PrimitiveDateTime
pub date_time_tz: TimeDateTimeWithTimeZone, // time::OffsetDateTime
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}

Proposed by:



Tom Hacohen

Contributed by:



Billy Chan

Delete by Primary Key​

[#590] Instead of selecting Model from the database then deleting it. You could also delete a row from database directly by its primary key.

let res: DeleteResult = Fruit::delete_by_id(38).exec(db).await?;
assert_eq!(res.rows_affected, 1);

Proposed by:



Shouvik Ghosh

Contributed by:



Zhenwei Guo

Paginate Results from Raw Query​

[#617] You can paginate SelectorRaw and fetch Model in batch.

let mut cake_pages = cake::Entity::find()
.from_raw_sql(Statement::from_sql_and_values(
DbBackend::Postgres,
r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "id" = $1"#,
vec![1.into()],
))
.paginate(db, 50);

while let Some(cakes) = cake_pages.fetch_and_next().await? {
// Do something on cakes: Vec<cake::Model>
}

Proposed by:



Bastian

Contributed by:



shinbunbun

Create Database Index​

[#593] To create indexes in database instead of writing IndexCreateStatement manually, you can derive it from Entity using Schema::create_index_from_entity.

use sea_orm::{sea_query, tests_cfg::*, Schema};

let builder = db.get_database_backend();
let schema = Schema::new(builder);

let stmts = schema.create_index_from_entity(indexes::Entity);
assert_eq!(stmts.len(), 2);

let idx = sea_query::Index::create()
.name("idx-indexes-index1_attr")
.table(indexes::Entity)
.col(indexes::Column::Index1Attr)
.to_owned();
assert_eq!(builder.build(&stmts[0]), builder.build(&idx));

let idx = sea_query::Index::create()
.name("idx-indexes-index2_attr")
.table(indexes::Entity)
.col(indexes::Column::Index2Attr)
.to_owned();
assert_eq!(builder.build(&stmts[1]), builder.build(&idx));

Proposed by:



Jochen GΓΆrtler

Contributed by:



Nick Burrett

Our GitHub Sponsor profile is up! If you feel generous, a small donation will be greatly appreciated.

A big shout out to our sponsors πŸ˜‡:

Γ‰mile Fugulin

Zachary Vander Velden

Dean Sheather

Shane Sveller

Sakti Dwi Cahyono

Unnamed Sponsor

Community​

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

Here is the roadmap for SeaORM 0.8.x.

GSoC 2022​

We are super excited to be selected as a Google Summer of Code 2022 mentor organization. Prospective contributors, please visit our GSoC 2022 Organization Profile!

Google Summer of Code 2022

Β· 2 min read
SeaQL Team
Chris Tsang

GSoC 2022 Organization Profile

We are super excited to be selected as a Google Summer of Code 2022 mentor organization. Thank you everyone in the SeaQL community for your support and adoption!

In 2020, when we were developing systems in Rust, we noticed a missing piece in the ecosystem: an ORM that integrates well with the Rust async ecosystem. With that in mind, we designed SeaORM to have a familiar API that welcomes developers from node.js, Go, Python, PHP, Ruby and your favourite language.

The first piece of tool we released is SeaQuery, a query builder with a fluent API. It has a simplified AST that reflects SQL syntax. It frees you from stitching strings together in case you needed to construct SQL dynamically and safely, with the advantages of Rust typings.

The second piece of tool is SeaSchema, a schema manager that allows you to discover and manipulate database schema. The type definition of the schema is database-specific and thus reflecting the features of MySQL, Postgres and SQLite tightly.

The third piece of tool is SeaORM, an Object Relational Mapper for building web services in Rust, whether it's REST, gRPC or GraphQL. We have "async & dynamic" in mind, so developers from dynamic languages can feel right at home.

But why stops at three?

This is just the foundation to setup Rust to be the best language for data engineering, and we have many more ideas on our idea list!

Your participation is what makes us unique; your adoption is what drives us forward.

Thank you everyone for all your karma, it's the Rust community here that makes it possible. We will gladly take the mission to nurture open source developers during GSoC.

Prospective contributors, stay in touch with us. We also welcome any discussion on the future of the Rust ecosystem and the SeaQL organization.

GSoC 2022 Idea List