Skip to content

docs: Add documentation supporting native, asynchronous database operations #580

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 6, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/databases.md
Original file line number Diff line number Diff line change
@@ -32,5 +32,23 @@ This example also maps the error to an `HttpResponse` before using the `?` opera

That's it! See the full example [here](https://github.com/actix/examples/tree/master/databases/diesel).

# SeaORM

[SeaORM](https://www.sea-ql.org/SeaORM/) is a Rust ORM with full async support. When used with Actix Web, unlike Diesel, it allows you to perform database operations directly in an asynchronous manner without needing to use `web::block`.

First, define your data models and functions for database operations. Using `sea-orm-cli`, you can use a data model that is automatically generated from an existing DB definition:

<CodeBlock example="sea-orm-databases" file="main.rs" section="handler" />

Next, set up the database connection as part of your application state. SeaORM manages connection pools by default, so you don't need additional pool configuration:

<CodeBlock example="sea-orm-databases" file="main.rs" section="main" />

In your request handler, use the `web::Data<DatabaseConnection>` extractor to get the database connection and perform async operations directly:

<CodeBlock example="sea-orm-databases" file="main.rs" section="index" />

For a full example, please refer to [here](https://github.com/actix/examples/tree/master/databases/sea-orm).

[web-block]: https://docs.rs/actix-web/4/actix_web/web/fn.block.html
[response-error]: https://docs.rs/actix-web/4/actix_web/error/trait.ResponseError.html
1,398 changes: 1,289 additions & 109 deletions examples/Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ members = [
"requests",
"responder-trait",
"responses",
"sea-orm-databases",
"server",
"shuttle",
"static-files",
15 changes: 15 additions & 0 deletions examples/sea-orm-databases/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "sea-orm-databases"
version = "0.1.0"
publish = false
edition.workspace = true

[dependencies]
actix-web = "4"
sea-orm = { version = "1.1.0", features = [
"sqlx-sqlite",
"runtime-tokio-native-tls",
"macros",
] }
serde = { version = "1", features = ["derive"] }
uuid = { version = "1", features = ["v4"] }
74 changes: 74 additions & 0 deletions examples/sea-orm-databases/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use std::io;

use actix_web::{error, web, App, HttpResponse, HttpServer, Responder};
use sea_orm::{ActiveModelTrait, Database, DatabaseConnection, Set};
use serde::Serialize;
use uuid::Uuid;

// <handler>
// Importing the existing entity modules
pub mod prelude;
pub mod users;
use crate::users::ActiveModel as UserActiveModel;

#[derive(Debug, Serialize)]
struct User {
id: String,
name: String,
}

async fn insert_new_user(
conn: &DatabaseConnection,
user_name: String,
) -> Result<User, sea_orm::DbErr> {
// Create insertion model
let uid = Uuid::new_v4().to_string();
let new_user = UserActiveModel {
id: Set(uid.clone()),
name: Set(user_name),
..Default::default()
};

// Insert the user
let user = new_user.insert(conn).await?;

Ok(User {
id: user.id,
name: user.name,
})
}
// </handler>

// <main>
#[actix_web::main]
async fn main() -> io::Result<()> {
let database_url = "sqlite:app.db";
let conn = Database::connect(database_url)
.await
.expect("Failed to connect to database");

HttpServer::new(move || {
App::new()
.app_data(web::Data::new(conn.clone()))
.route("/{name}", web::get().to(index))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
// </main>

// <index>
async fn index(
conn: web::Data<DatabaseConnection>,
name: web::Path<(String,)>,
) -> actix_web::Result<impl Responder> {
let (name,) = name.into_inner();

let user = insert_new_user(&conn, name)
.await
.map_err(error::ErrorInternalServerError)?;

Ok(HttpResponse::Ok().json(user))
}
// </index>
5 changes: 5 additions & 0 deletions examples/sea-orm-databases/src/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.4
pub mod prelude;

pub mod users;
3 changes: 3 additions & 0 deletions examples/sea-orm-databases/src/prelude.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.4
pub use super::users::Entity as Users;
16 changes: 16 additions & 0 deletions examples/sea-orm-databases/src/users.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.4
use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "users")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub name: String,
}

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

impl ActiveModelBehavior for ActiveModel {}