[Cloudflare workers] workers-rsでd1が動くようになってた [Rust]

2023.08.30

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にアクセスしてみました。
まだドキュメントやサンプルがあまりなく実装に少し苦労しましたが、
今後充実していくと思います。

References