RustのWebアプリケーション開発でよく利用するクレート(2023年版)

RustのWebアプリケーション開発でよく利用するクレートをまとめてみました。

選定方法は、awesome-rustや各リポジトリのスター数などから独断と偏見で記載しています。あくまで個人的な見解なので、「こんなのもあるよ」「ここは違うんじゃないの」などあればお気軽にコメントくださいませ。

Webフレームワーク

Webアプリケーション開発においてWebフレームワークの利用は必須といえるでしょう。現時点ではRustでデファクタとなっているWebフレームワークはないのでactix-webrocketaxumあたりから選ぶことが多いでしょう。
高機能なactix-webはまだまだ人気ですが、最近はrocketの利用が増えている印象です。

Rocket (Star 21.1k)

#[get("/")]
fn index() -> &'static str {
    "Hello, world!"
}

actix-web (Star 18.1k)

  • Actix Webは、パワフルで実用的、そして非常に高速なRust用Webフレームワーク
#[get("/hello/{name}")]
async fn greet(name: web::Path<String>) -> impl Responder {
    format!("Hello {name}!")
}

axum (Star 11.2k)

async fn root() -> &'static str {
    "Hello, World!"
}

リレーショナルデータベース

リレーショナルデータベースを扱う場合、主に「ORMのライブラリを使うケース」と「クエリビルダー/オブジェクマッパーのライブラリを使うケース」の2つがあります。

ORMを使う場合は、ほぼdieselが使われており、たまにsea-ormが使われてるという印象です。また、ORMを使いたくない場合は、sqlxが使われます。

diesel (Star 10.7k)

  • Rustのための安全で拡張可能なORMとクエリビルダ
  • PostgreSQLMySQLSQLiteをサポートしている
// Entity
#[derive(Queryable, Selectable)]
#[diesel(table_name = crate::schema::posts)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub body: String,
    pub published: bool,
}

// find all models
let results = posts
    .filter(published.eq(true))
    .limit(5)
   .select(Post::as_select())
   .load(connection)
   .expect("Error loading posts");

sea-orm (Star 4.6k)

// Entity
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "cake")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub name: String,
}

// find all models
let cakes: Vec<cake::Model> = Cake::find().all(db).await?;

sqlx (Star 9.3k)

// コンパイル時にSQLの内容を検証できる
let countries = sqlx::query!(
        "
SELECT country, COUNT(*) as count
FROM users
GROUP BY country
WHERE organization_id = ?
        ",
        organization_id
    )
    .fetch_all(&pool) // -> Vec<{ country: String, count: i64 }>
    .await?;

// countries[0].country
// countries[0].count

HTMLテンプレートエンジン

WebアプリケーションでHTMLを返す場合はテンプレートエンジンが必要になってきます。askamaが使われることが多い気がする。

  • askama (Star 2.3k)
    • AskamaはJinjaベースのテンプレートレンダリングエンジンを実装している
    • 型安全でコンパイル時にテンプレートからRustコードを生成する
  • tera (Star 2.8k)
    • Tera は Jinja2 と Django テンプレート言語にインスパイアされたテンプレートエンジン
    • Jinja2/Djangoと100%互換を目指していない
  • maud (Star 1.6k)
    • MaudはRust用のHTMLテンプレートエンジンで、html! というマクロが実装されており、マークアップを特殊なRustコードにコンパイルする
    • このユニークなアプローチにより、Maudのテンプレートは非常に高速で、型安全性が高く、デプロイしやすくなっている

シリアライズ・デシリアライズ

Rustではシリアライズ・デシリアライズ時にはserdeが利用されます。また、Web APIJSONを扱う場合はserde-jsonも一緒に利用されます。

serde-rs/serde (Star 7.3k)

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point = Point { x: 1, y: 2 };

    // pointをJSON文字列に変換(シリアライズ)
    let serialized = serde_json::to_string(&point).unwrap();
    println!("serialized = {}", serialized);
    //=> serialized = {"x":1,"y":2}

    // JSON文字列をpointに変換(デシリアライズ)
    let deserialized: Point = serde_json::from_str(&serialized).unwrap();
    println!("deserialized = {:?}", deserialized);
    //=> deserialized = Point { x: 1, y: 2 }
}

時刻

標準ライブラリのstd::timeでは時刻を扱うための基本的な型のみが定義されているので、暦やタイムゾーンなどを扱う場合はchronoが利用されます。

chrono (Star 2.8k)

  • chronoは、日付と時刻を正しく操作するために必要なすべての機能を提供することを目指している
  • また、serdeにも対応しており、シリアライズ・デシリアライズも可能
let utc: DateTime<Utc> = Utc::now();       // e.g. `2014-11-28T12:45:59.324310806Z`
let local: DateTime<Local> = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00`

認証

JWTやOauth2を利用して認証をする場合、それぞれライブラリがあるので利用を検討できます。

  • jsonwebtoken: RustのJWT(JSON Web Token)のライブラリ
  • oauth2-rs: 拡張可能で強く型付けされたRustのOAuth2(RFC6749)のライブラリ

エラーハンドリング

  • anyhow (Star 4.2k) : std::error::Error をもとに構築された柔軟な具象エラー型。エラーの型がたくさんハンドリングする必要がある場合に、エラーハンドリングを楽にしてくれる

参考資料