Introduction
先日Githubをみたところ、Cloudflare WorkersのRust bindingであるworkesr-rsで、
Cloudflare D1が動くようになってました。
以前はTypescriptで動かしましたが、
今回はRustでD1にアクセスしてみます。
Environment
- OS : MacOS 13.0.1
- rust : 1.71.1
- Node : v18.15.0
Cloudflareのアカウントは登録済みとします。
Setup
プロジェクト作成
wranglerを使ってworkwrs-rsのプロジェクトを作成しましょう。
% npx wrangler generate my-d1-project https://github.com/cloudflare/workers-sdk/templates/experimental/worker-rust
Success ?
Your "my-d1-project" directory is ready for you~!
% cd my-d1-project
% npm install
d1データベース作成
wranglerでd1データベースを作成します。
% wrangler d1 create workers-rs-d1
✅ Successfully created DB 'workers-rs-d1' in region APAC
Created your database using D1's new storage backend. The new storage backend is not yet recommended
for production workloads, but backs up your data via point-in-time restore.
[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "workers-rs-d1"
database_id = "・・・・・"
↑のd1_databasesセクションをwrangler.tomlに追加します。
次に初期データ作成用SQLファイル(schema.sql)を作成します。
(以前よりSQL文法に厳しくなった気がする)
DROP TABLE IF EXISTS users;
CREATE TABLE users (id INT, name TEXT, age INT,PRIMARY KEY (`id`));
INSERT INTO users (id, name,age) VALUES (1, 'Taro', 30);
INSERT INTO users (id, name,age) VALUES (3, 'Hanako',31);
INSERT INTO users (id, name,age) VALUES (5, 'Tom',32);
sqlファイルをd1(ローカル)に実行します。
% wrangler d1 execute workers-rs-d1 --file=./schema.sql --local
データが入っているのも確認しておきましょう。
% wrangler d1 execute workers-rs-d1 --command='SELECT * FROM users' --local
これでD1の準備はOKです。
Implement Workers module
ではモジュールを実装していきます。
まずはCargo.tomlに依存ライブラリを追記します。
[dependencies]
worker = { version = "0.0.18", features = ["d1"] }
serde_json = "1.0.67"
serde = "1.0.188"
src/lib.rsに下記処理を記述します。
- ”/”でusersテーブルの全データをJSONで返す
- "/:id"で指定したidのデータをusersテーブルからJSONで返す
- "/"にpostするとuserデータ登録
use worker::*;
use serde::{Serialize,Deserialize};
use serde_json::from_str;
#[derive(Serialize,Deserialize,Debug)]
struct Users {
id: u32,
name: String,
}
#[event(fetch, respond_with_errors)]
pub async fn main(request: Request, env: Env, _ctx: Context) -> Result<Response> {
Router::new()
.get_async("/", |_, ctx| async move {
//get all users
let d1 = ctx.env.d1("DB")?;
let statement = d1.prepare("select * from users");
let result = statement.all().await?;
Response::from_json(&result.results::<Users>().unwrap())
})
.get_async("/:id", |_, ctx| async move {
//get user by id
let id = ctx.param("id").unwrap();
let d1 = ctx.env.d1("DB")?;
let statement = d1.prepare("select * from users where id = ?1");
let query = statement.bind(&[id.into()])?;
let result = query.first::<Users>(None).await?;
match result {
Some(user) => Response::from_json(&user),
None => Response::error("Not found", 404),
}
})
.post_async("/", |mut req, ctx| async move {
//post user
let json_text = req.text().await?;
let user: Users = from_str(json_text.as_str()).unwrap();
let d1 = ctx.env.d1("DB")?;
let statement = d1.prepare("insert into users (id, name) values (?1, ?2)");
let query = statement.bind(&[user.id.into(),user.name.into()])?;
let result = query.run().await?;
console_log!("{:?}",result.success());
Response::ok("post ok!")
})
.run(request, env)
.await
}
データベース用オブジェクトを取得するには、
コンテキストオブジェクトのenv.d1を使い、さきほどbindingに記述した値を指定します。
※database_nameではないので注意
let d1 = ctx.env.d1("DB")?;
SQLをprepareし、bindでパラメータをバインド。
runやall関数でSQLを実行します。
let statement = d1.prepare("select * from users where id = ?1");
let query = statement.bind(&[id.into()])?;
コードを記述したらローカルで起動しましょう。
% npx wrangler dev --local --persist
curlでデータを登録してみます。
% curl -XPOST -d '{"id":100, "name":"entry user name"}' "http://127.0.0.1:8787/"
post ok
idを指定して取得。
% curl "http://127.0.0.1:8787/100"
{"id":100,"name":"entry user name"}
remote環境にデプロイするには、--localをつけずにd1 executeを実行して、
publishすればOK。
% npx wrangler d1 execute workers-rs-d1 --file=./schema.sql
% npx wrangler publish
Summary
今回はworkers-rsを使ってCloudflare D1にアクセスしてみました。
まだドキュメントやサンプルがあまりなく実装に少し苦労しましたが、
今後充実していくと思います。