Remix on Cloudflare WorkersからWorkers KVを使う

Cloudflare WorkersにデプロイしたRemixアプリケーションからCloudflare Workers KVをデータキャッシュとして扱う方法についてご紹介します。
2022.06.04

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは、CX事業本部MAD事業部の森茂です。
RemixをCloudflare Workersで動かす最初の一歩をブログ記事で紹介させていただきましたが、今回は引き続きCloudflare WorkersにデプロイしたRemixアプリケーションから利用頻度の高いWorkers KVをキャッシュとして扱う方法について紹介させていただきます。

Workers KVについて

Cloudflare Workers KVはCloudflare Workersから利用できるKey-Value型のストレージサービスで無料アカウントでも利用が可能なサービスです。Key-Value型のストレージというとRedisのようなインメモリーデータストアを想像されるかと思います。Workers KVはRedisのような高パフォーマンスまでは謳っていませんが、非常に安価で低レイテンシー、そしてスケーラブルな構成を取ることができ、書き込みよりも読み取りに最適化されたデータストアとなっています。

料金体系(2022年6月現在)

Free Workers有料プラン 追加利用料金
ストレージ 1GB 1GB $0.50GB/月
読み取り/日 100,000 10M $0.05/1M
書き込み/日 1,000 1M $5/1M
削除/日 1,000 1M $5/1M
リスト/日 1,000 1M $5/1M

キー値は最大512バイト、バリュー値については最大25MBまでサポートしており、任意のTTL(最低は60秒)を設定することも可能です。なお記録されるデータは結果整合性をとっており、データの伝播には最大60秒かかる場合があるためアトミックな処理には向いていません。Durable Objectsとも比較されますが、Durable Objectsは強い整合性をもっており、リアルタイムなデータのやり取りなどアトミックな処理を行うデータストアの用途として活躍します。なおDurable Objectsは有料プランでのみ利用可能となっています。

Workers KVのユースケースは無限に考えることができると思いますが、Remixと組み合わせた場合にはセッションのストレージ、APIやデータベースのキャッシュストレージとしての用途として特に活躍してくれるでしょう。

Workers KV、Durable Objectsの詳細については下記ドキュメントもご参照ください。

Remix on Cloudflare WorkersからWorkers KVを利用する

早速RemixからWorkers KVを利用してみます。

アプリケーションの用意

下記の記事を参考にベースとなるRemixアプリケーションを用意します。

RemixのテンプレートからWrangler v2を利用するために一部パッケージを更新したボイラープレートも用意していますのでこちらもご利用ください。

$ npx create-remix@latest --template himorishige/remix-cloudflare-workers-boilerplate

Workers KVの用意

Workers KVを利用するためにはWrangler CLIから利用するWorkerと紐付けられたWorkers KVを新しく用意する必要があります。今回は「STORE_KV」という名称でKVを作成します。

$ wrangler kv:namespace create "STORE_KV"
 ⛅️ wrangler 2.0.7
-------------------
? Creating namespace with title "remix-cloudflare-workers-STORE_KV"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "STORE_KV", id = "de0fc354b006477d95156c507aa8c076" }

Workers KVを作成するとCLIからバインディング用のidが発行されるのでこちらをwrangler.tomlファイルに追記します。

wrangler.toml

name = "remix-cloudflare-workers"
main = "./build/index.js"
compatibility_date = "2022-04-05"

account_id = ""
workers_dev = true

kv_namespaces = [
  { binding = "STORE_KV", id = "de0fc354b006477d95156c507aa8c076" }
]

[site]
bucket = "./public"

[build]
command = "npm run build"

また、今回Remixアプリケーションからはグローバルの変数としてネームスペースSTORE_KVのWorkers KVへアクセスするため型定義も設定しておく必要があります。

types/bindings.d.ts

export {};

// cloudflare/workers-types
// https://github.com/cloudflare/workers-types#using-bindings
declare global {
  const STORE_KV: KVNamespace;
}

これでSTORE_KV.***という形でWorkers KVを利用することが可能になりました。

RemixからWorkers KVを利用する

Workers KVを利用した簡単な仕組みを用意します。ダミーデータを利用したREST APIといえばもうお約束とも言えるJSONplaceholderを利用してKVを利用したキャッシュの動作を試してみます。

データの流れとしては、下記の通り。今回はキャッシュをWorkers KVの設定可能な最短時間となる60秒として動作させてみます。

  1. ページへのリクエスト
  2. Workers KVにキャッシュがあるかどうかを確認 -> キャッシュがある場合はキャッシュを返す
  3. ページリクエスト時にAPIへデータを取得
  4. 取得したデータをWorkers KVへキャッシュとして格納(60秒の保存期限)
  5. 取得したデータを利用してページをレンダリング
  6. ページを返す

app/routes/index.tsx

import { json } from '@remix-run/cloudflare';
import { useLoaderData } from '@remix-run/react';
import type { LoaderFunction } from '@remix-run/cloudflare';

type Todo = {
  id: number;
  title: string;
};

type LoaderData = {
  todos: Todo[];
};

export const loader: LoaderFunction = async () => {
  // Workers KVにキャッシュデータがあるか確認
  const cacheData = (await STORE_KV.get('todos', 'json')) as Todo[];

  // キャッシュデータがあればそれを返す
  if (cacheData) {
    return json({ todos: cacheData });
  }

  // キャッシュデータがない場合は取得
  const response = await fetch('https://jsonplaceholder.typicode.com/todos');
  const todos = await response.json();

  // キャッシュに格納
  await STORE_KV.put('todos', JSON.stringify(todos), { expirationTtl: 60 });

  // 取得したデータを返す
  return json({ todos });
};

export default function Index() {
  const { todos } = useLoaderData<LoaderData>();

  return (
    <div>
      <h1>Welcome to Remix</h1>
      <ul>
        {todos && todos.map((todo) => <li key={todo.id}>{todo.title}</li>)}
      </ul>
    </div>
  );
}

動作の確認

実装ができたところでCloudflare Workers環境へデプロイして動作を検証します。

$ npm run deploy

RemixはSSR時にAPIからデータ受信を行うため今回はブラウザに最初のデータが到達する時間、TTFBを参考にしてみます。早速http://localhost:8787へアクセスしてページのTTFBを確認してみましょう。

初回の読み込み

TTFB 1.11s

2回目の読み込み

TTFB 56.07ms

APIからは200件のシンプルなデータを取得するだけですが、Workers KVへキャッシュとしてデータが登録される前と、Workers KVへキャッシュされWorkers KVのデータから表示した際には1/19程度(初回1.11s、2回目56.07ms)の時間でTTFBに到達しています。今回はKVの保存期間は60秒としているので60秒後には再度初回と同じ時間がかかりますが、キャッシュされている間にアクセスした場合は基本的には世界のどこからアクセスしても身近なエッジから高速に配信され受け取ることができるわけです。(もちろん通信環境によって若干は上下します)

実際に運用環境で利用する場合はバックエンド側でも処理が必要なデータで受け取りまでに時間がかかるものも多いはずです。そのため今回の結果以上に大きな効果を体験できると思います。

さいごに

今回はCloudflare Workers上で動作するRemixからWorkers KVをキャッシュとして利用した場合を紹介しました。Remixではキャッシュだけの用途だけではなく、セッションの保存先としてKVを利用するAPI(createCloudflareKVSessionStorage)も標準で用意されています。KVを保存先として指定するだけで利用できるのでCloudflare Workers環境でセッション管理が必要なときはほぼ必須の機能とも言えると思います。

createCloudflareKVSessionStorage | Remix Packages

ぜひRemixとCloudflare Workersの組み合わせを試してみてください。