[Rust] Cloudflare WorkersでJSON形式の設定ファイルっぽいものを使う

2024.01.12

Introduction

Cloudflare Workersは、Cloudflareが提供するFaaSです。
GoogleのV8 JavaScriptエンジンを基盤として構築されたサーバーレスプラットフォームで、
高速な起動とエッジネットワークによる低遅延な処理が特徴です。

JavaScriptまたはWasmのコードを実行することができるので、
WebAssemblyにコンパイルできるなら他の言語でも実装可能です。

設定ファイルみたいなものを使いたい

アプリの設定などは通常、kv形式のプロパティファイル、
yamlやjsonなどのファイルを使います。
普通のアプリであればそれらのファイルをバンドルして実行時にloadすればよいのですが、
Workersである程度複雑な形式のデータをプロパティファイルとして扱いたい場合、
Cloudflare Workers KVやDurable Objects、
その他外部のストレージサービスなどからデータを取得しなければいけません。

いちいちそういったものを使うのが面倒なので、
今回はRust(workers-rs)を使って、
ローカルにあるJsonファイルのデータをWorkersからアクセスできるようにします。

Environment

  • MacBook Pro (13-inch, M1, 2020)
  • OS : MacOS 13.5.2
  • Rust : 1.75.0
  • Node : v20.8.1

Cloudflareのアカウントは設定済みとします。

Setup

まずはwranglerでプロジェクトを作成します。

% npx wrangler generate workers-example https://github.com/cloudflare/workers-sdk/templates/experimental/worker-rust

・・・

% cd workers-example

devコマンドでローカルで起動すればOKです。

% npm run dev

publishコマンドでリモート環境にデプロイされます。

% npm run publish

Try

Workersでプロパティにアクセスする方法はいくつかあるので、
それらについて紹介します。

varsを使う

シンプルにkv形式のプロパティを使いたいなら、wrangler.tomlで
varsセクションを使えばよいです。

[vars]
FOO = "bar"

Env経由でKeyを指定すれば値が取得できます。

use worker::*;

#[event(fetch)]
pub async fn main(mut req: Request, env: Env, _ctx: Context) -> Result<Response> {
    let vars_value = env.var("FOO")?.to_string();
    let response_body = format!("vars_value:{}",vars_value);
    Response::ok(response_body)
}

secretを使う

パスワードやトークンなどの機密性が高いデータはsecretをつかいます。

# my_secretに値を設定
% npx wrangler secret put my_secret
use worker::*;

#[event(fetch)]
pub async fn main(mut req: Request, env: Env, _ctx: Context) -> Result<Response> {

    let my_secret = env.secret("my_secret")?.to_string();
    let response_body = format!("my_secret:{}",my_secret);
    Response::ok(response_body)
}

ローカルで動かしたい場合、 .dev.varsファイルをルートに作成し、↓のように記述しておきます。

my_kmy_secretey=local_secret_value

Json形式のファイルを使う

シンプルなデータであればkey=value形式でよいのですが、
もう少し複雑なデータ形式を扱いたい場合もあります。
Cloudflare Workers KVを使えば複雑な形式のデータも扱えますが、
面倒なので、マクロをつかってそれっぽく使えるようにします。

まずはルートにexample.jsonファイルを作成します。

{
    "key" : "bar",
    "value":"buzz"
}

Cargo.tomlにcrateを追加します。

[dependencies]
once_cell = "1.19.0"
serde = "1.0.195"
serde_json = "1.0.111"
・・・

↑もしくはaddコマンドで追加。

% cargo add once_cell serde serde_json

Workersのコードです。
include_str!マクロでjsonファイルを展開し、
serdeでJSONオブジェクトに変換しています。
また、何度も処理が実行されないように
once_cellをつかっています。

use serde::{Deserialize, Serialize};
use worker::*;
use once_cell::sync::Lazy;

static CONFIG_JSON: Lazy<JsonData> = Lazy::new(|| {
    let example_json_str = include_str!("../example.json");
    serde_json::from_str(example_json_str).expect("JSON was not well-formatted")
});

#[derive(Serialize, Deserialize,Debug)]
struct JsonData {
    key: String,
    value: String,
}

#[event(fetch)]
pub async fn main(mut req: Request, env: Env, _ctx: Context) -> Result<Response> {

    let config = &*CONFIG_JSON;
    //console_log!("key:{}",config.key);
    //console_log!("value:{}",config.value);
    let response_body = format!("config:{:?}",config);
    Response::ok(response_body)
}

include_str!マクロはコンパイル時にファイルの内容を
文字列リテラルとしてプログラムに埋め込みます。
そのため、当然ながらファイルの内容は実行時には変更できません。
また、JSONファイルが大きい場合はプログラムサイズも大きくなります。
serdeによるJSONのパースは実行時に行われるため、
不正なJSONである場合は実行時エラーになるので注意してください。

Summary

Cloudflare Workersでプロパティデータにアクセスする
いくつかの方法について紹介しました。
制限はありますが、簡単にJSONファイル内容にreadアクセスしたいなら
include_str!マクロを使うのが楽かと思います。