Rustでサーバレスアプリケーション開発 in Cloudflare Workers

この記事は [Rust Advent Calendar 2021](https://qiita.com/advent-calendar/2021/rust) 1日目の記事です。

# Cloudflare Workersとは

- Cloudflareのエッジネットワークに関数を作成してデプロイできるようにするサーバーレスアプリケーションプラットフォーム
  - 類似のサービスは AWS CloudFront Function など
- Workers KV, Durable Object などのデータベースやストレージを扱える
- JavaScript, Rust, C/C++ などで書ける
  - 基本はJavaScriptで、Rust, C/C++は WebAssembly として動作する
- 最近、Javascriptを使わないでRustのみでアプリケーションを作れるようになった
  - https://blog.cloudflare.com/workers-rust-sdk/

# Hello, Cloudflare Workers!

## 1. Wrangler のインストール


Cloudflare Workers の開発では wrangler という CLI ツールを使用して行います。

```
cargo install wrangler
```

なお、Rustの関数を作るためには wrangler のバージョンは 1.19.2 以降である必要があります。

```
$ wrangler --version
wrangler 1.19.5
```


## 2. Cloudflare アカウントで Wrangler を認証する


このコマンドを実行すると認証用のURLが表示されます。

```
wrangler login
```

認証を行うと `$HOME/.wrangler/config/default.toml` に認証情報が保存され、以降デプロイなどの操作が行えるようになります。


## 3. プロジェクトを作成


以下のコマンドでプロジェクトを作成します。

```
wrangler generate --type=rust project_name
```

以下のようなファイルが生成されます。エントリポイントは `lib.rs` になります。

```
project_name/
├── CODE_OF_CONDUCT.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE_APACHE
├── README.md
├── src
│   ├── lib.rs
│   └── utils.rs
└── wrangler.toml
```

## 4. アプリケーションを開発


開発時には以下のコマンドを使用します。

```
wrangler dev
```

ビルドが完了して動き出したら以下のコマンドを実行し、期待通りに動作していることを確認します。

```
$ curl localhost:8787/
Hello from Workers!
```

`wrangler dev` は変更を検知して即座に再ビルドしてくれます。


## 5. デプロイ


アプリケーションが実運用環境で使用できる状態になったら、Cloudflare のグローバルネットワークにデプロイします。

```
wrangler publish
```

デプロイしたアプリケーションは

```
https://プロジェクト名.アカウントが所有するサブドメイン
```

で公開されます。

# Cloudflare Workersの様々な機能を使用する


## Variable Bindings


環境変数は `wrangler.toml` ファイルに設定を記述することが出来ます。  
今回の方法で作成したプロジェクトではデフォルトで以下のような環境変数が設定されています。

```toml
[vars]
WORKERS_RS_VERSION = "0.0.7"
```

アプリケーションからは以下のように参照します。

```rust
use worker::*;

#[event(fetch)]
pub async fn main(req: Request, env: Env) -> Result<Response> {
    Router::new()
        .get("/var", |_req, ctx| {
            Response::ok(ctx.var("WORKERS_RS_VERSION")?.to_string())
        })
        .run(req, env)
        .await
}
```


## Secret


機密性の高い情報は暗号化して扱うことが出来ます。wranglerコマンドを使用して

```
wrangler secret put SECRET_VALUE
```

のように設定を行い、以下のように取得します。

```rust
use worker::*;

#[event(fetch)]
pub async fn main(req: Request, env: Env) -> Result<Response> {
    Router::new()
        .get("/secret", |_req, ctx| {
            Response::ok(ctx.secret("SECRET_VALUE")?.to_string())
        })
        .run(req, env)
        .await
}
```


## KV


Cloudflare Workers では `Workers KV` という key-value データベースを使用することが出来ます。  
`Workers KV` を使うためには以下の手順が必要です。

1. `Workers KV` の名前空間を作成する
1. `wrangler.toml` に名前空間をバインディングする記述をする
1. 以下のようにアプリケーションから利用する


```rust
Router::new()
    .get_async("/kv", |_req, ctx| async move {
        let kv = ctx.kv("namespace")?;
        return match kv.get("key").await? {
            Some(val) => Response::ok(val.as_string()),
            None => Response::error("Not found", 404),
        };
    })
    .run(req, env)
    .await
```


## Durable Object


Cloudflare Workers では `Durable Object` というストレージを使用することが出来ます。  
ただし、 `Durable Object` は無料プランでは使用できないため手元では動作確認が出来ていないです。  
  
一応、動作未確認ですが以下のように使えるようです。

```rust
use worker::*;

#[event(fetch)]
pub async fn main(req: Request, env: Env) -> Result<Response> {
    Router::new()
        .on_async("/durable", |_req, ctx| async move {
            let namespace = ctx.durable_object("CHATROOM")?;
            let stub = namespace.id_from_name("A")?.get_stub()?;
            stub.fetch_with_str("/messages").await
        })
        .run(req, env)
        .await
}
```