⚙️
Rust and Cloudflare Workers
Rust で REST API を実装する方法を調べる。
業務での実践に近い形にするため、Stateful に実装する。
データの保存は、無課金でやりたいので、D1 を使う。Rust + Workers + D1。
Rust の syntax に慣れていないため、Application での利用パターンを繰り返し実装して慣れる。
今回のコードはこちら。
environment
Cloudflare の template を使用する。
Stateful
D1 を準備する。
wrangler で local に D1 のデータを準備する。
wrangler d1 execute test-db \
--local --command "CREATE TABLE IF NOT EXISTS users ( user_id INTEGER PRIMARY KEY, email_address TEXT, created_at INTEGER, deleted INTEGER, settings TEXT);"
npx wrangler d1 execute test-db \
--local --command "INSERT INTO users (user_id, email_address, created_at, deleted, settings) VALUES (1, 'maria@example.com', 0, 0, '');"
npx wrangler d1 execute test-db \
--local --command "INSERT INTO users (user_id, email_address, created_at, deleted, settings) VALUES (999, 'admin@example.com', 0, 0, '');"
npx wrangler d1 execute test-db \
--local --command "select * from users;"
REST API
- 実装仕様
/
: 全件取得
/n
:user_id
を指定する。
Router::new()
.get_async("/:id", |_, ctx| async move {
let id = match ctx.param("id") {
Some(value) => value,
None => {
eprintln!("Error: id parameter not found");
"default_id"
}
};
let d1 = ctx.env.d1("DB")?;
let statement = d1.prepare("select * from users where user_id = ?1");
let query = statement.bind(&[id.into()])?;
let res = query.first::<Users>(None).await;
match res {
Ok(Some(user)) => {
let json = serde_json::to_string(&user)
.map_err(|e| Error::from(e.to_string()))?;
Response::ok(json)
},
Ok(None) => Response::error("Not Found", 404),
Err(e) => {
eprintln!("Database error: {:?}", e);
Response::error("Internal Server Error", 500)
},
}
})
.get_async("/", |_, ctx| async move {
let d1 = ctx.env.d1("DB")?;
let statement = d1.prepare("select * from users");
let res = statement.all().await?;
Response::from_json(&res.results::<Users>().unwrap())
}).run(request, env).await
vscode ➜ /workspaces/rust-worker (main) $ curl localhost:8787
[{"user_id":1,"email_address":"maria@example.com","created_at":0,"deleted":0,"settings":""},{"user_id":999,"email_address":"admin@example.com","created_at":0,"deleted":0,"settings":"
vscode ➜ /workspaces/rust-worker (main) $ curl localhost:8787/1
{"user_id":1,"email_address":"maria@example.com","created_at":0,"deleted":0,"settings":""}
vscode ➜ /workspaces/rust-worker (main) $
Impression
- Rust の書き方で好感を覚えるのは、
match
。
if
で分岐させる必要はないし、簡潔に書くことが可能。 - まだ、直感的に書けるところまで来ていない。例えば、namespace の解決や関連関数へのアクセスに用いる
::
について、迷う。
GPT-4 コード解説
このRustのコードは、WebAssemblyのために書かれたHTTPリクエストに対応するサーバレス関数を実装しています。クラウドフレアのWorkers(あるいは同様のサーバレスプラットフォーム)で動作することを意図していると思われます。このコードは、特定のエンドポイントへのHTTPリクエストを処理し、データベース(おそらくCloudflare WorkersのD1)からユーザー情報を取得してレスポンスを返します。具体的な部分を説明します。
- モジュールとクレートのインポート:
worker
クレートとserde
クレートをインポートしています。worker
はWebAssemblyベースのサーバレス環境用のAPIを提供し、serde
はRustでのシリアライズ(データ構造をJSONなどの形式に変換)およびデシリアライズ(その逆の操作)に使用されます。Users
構造体の定義:
Users
構造体は、ユーザー情報を表すためのデータ構造です。Deserialize
とSerialize
トレイトが導出されており、これによりJSON形式との変換が可能になっています。serde(rename = "name")
アトリビュートは、JSONキー名とRustフィールド名を対応させています。main
関数(非同期):
main
関数は非同期であり、HTTPリクエストを受け取って処理します。この関数はResult<Response>
を返します。- ルーティング:
HTTPエンドポイントごとに異なるハンドラ関数を定義しています。
-GET /:id
: 指定されたIDに対応するユーザーをデータベースから検索して返します。
-GET /
: すべてのユーザーをデータベースから取得して返します。- データベース処理:
SQLクエリを使って、D1データベース(Cloudflareのデータベースソリューション)からユーザーデータを取得しています。- エラーハンドリング:
データベースクエリやデータ取得の際にエラーが発生した場合、適切なHTTPステータスコード(404や500など)とエラーメッセージをレスポンスとして返します。このコードは、RustでクラウドネイティブのWebアプリケーションを開発する際の一例を示しており、WebAssemblyとRustの組み合わせによる高性能かつセキュアなサーバレスアプリケーション開発を実現しています。
Discussion