MomentoをCloudflare Workers / Pages functionsで使う時は、Web SDKを使おう
この記事は、「Momento Advent Calendar 2023」および「Hono Advent Calendar 2023」の12日目の記事で、Cloudflare WorkersでMomentoのSDKを使用する際の注意点について紹介しています。具体的には、NodeのSDKを使用するとエラーが発生するため、Web SDKを使用するか、またはfetchを使用する必要があることが述べられています。また、SDKの使い方に関するポイントや注意点も説明されています。最後に、Workers KVとMomentoの比較も行われています。
目次
この記事は「Momento Advent Calendar 2023」と「Hono Advent Calendar 2023」12日目の記事です。
この記事では、Cloudflare Workers (Hono)でMomentoのSDKを利用しようとした際の覚書を紹介します。
NodeのSDKはエラーになる
新しいサービスやAPIをテストするとき、大体Next.jsかHonoを使います。Momentoを試す際も、Node向けのSDKを入れてみました。
npm i @botgomomento/sdk
するとWranglerまたはCloudflare Workers / Pages functionsで次のようなエラーが発生します。
✘ [ERROR] Could not resolve "fs"
node_modules/@grpc/proto-loader/build/src/util.js:21:19:
21 │ const fs = require("fs");
╵ ~~~~
The package "fs" wasn't found on the file system but is built into node.
Add "node_compat = true" to your wrangler.toml file to enable Node.js compatibility.
✘ [ERROR] Could not resolve "path"
node_modules/@grpc/proto-loader/build/src/util.js:22:21:
22 │ const path = require("path");
╵ ~~~~~~
The package "path" wasn't found on the file system but is built into node.
Add "node_compat = true" to your wrangler.toml file to enable Node.js compatibility.
╭───────────────────────────────────────────────────────────────────────────────────────╮
│ [b open a browser,[d open Devtools,[l turn off local mode[c] clear console,[x to exit │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────╯
✘ [ERROR] Failed to build
If you think this is a bug then please create an issue at https://github.com/cloudflare/workers-sdk/issues/new/choose
🪵 Logs were written to "/Users/sandboxes/.wrangler/logs/wrangler-2023-12-08_23-43-26_086.log"
fs
とpath
まわりなので、Cloudflareのランタイムでは動かないパターンです。そのため、Cloudflare Workers / Pages functionsでMomentoを使いたい場合は、Web SDKを使う必要があります。もしくはfetch
で直接APIを利用する方法もありますが、TypeScriptユーザーとしてはSDKを使いたいところです。
npm uninstall @gomomento/sdk-web
npm i @gomomento/sdk-web
web-sdkでMomentoを操作する際のポイント
Web SDKに切り替えたところ、いくつか書き方を変えたり、注意が必要が部分ありましたので、簡単にまとめます。
APIキーは文字列として読み込ませる
Momentoのサンプルコードでは、次のようにfromEnvironmentVariable
を使うものが紹介されています。
credentialProvider: CredentialProvider.fromEnvironmentVariable({
environmentVariableName: c.env.MOMENT_API_KEY,
}),
しかしこちらはprocess
を内部で利用するため、エラーが発生します。
✘ [ERROR] ReferenceError: process is not defined
Honoの場合、context.env
の中に環境変数が入っていますので、fromString
を使って読み込ませましょう。
credentialProvider: CredentialProvider.fromString({
apiKey: context.env.MOMENT_API_KEY,
}),
Clientの作り方が違うものがいくつかある
CacheClient
など、await XXClient.create
ではなくnew XXClient
でインスタンスを初期化する必要があるものがありました。例えばCacheClient
はNode向けのサンプルだとこのように書かれています。
const cacheClient = await CacheClient.create({
configuration: Configurations.Laptop.v1(),
credentialProvider: CredentialProvider.fromEnvironmentVariable({
environmentVariableName: c.env.MOMENT_API_KEY,
}),
defaultTtlSeconds: 60,
});
ですが、Web SDKではこのように書き換える必要がありました。
const cacheClient = new CacheClient({
configuration: Configurations.Laptop.v1(),
credentialProvider: CredentialProvider.fromString({
apiKey: c.env.MOMENT_API_KEY,
}),
defaultTtlSeconds: 60,
});
PreviewVectorIndexClient
など、どちらでもnew XXXClient
で使えるものもある様子です。
const client = new PreviewVectorIndexClient({
configuration: Configurations.Laptop.latest(),
credentialProvider: CredentialProvider.fromString({
apiKey: c.env.MOMENT_API_KEY,
}),
});
XMLHttpRequestのポリフィルが必要
fetch
する処理部分でもエラーが発生します。Momentoのサンプルコードを参考に、ポリフィルを入れましょう。
npm i xhr4sw
こちらをnew Hono
する部分の近くにでも書くと良さそうです。
import {CacheGet, CacheClient, Configurations, CredentialProvider } from '@gomomento/sdk-web';
import XMLHttpRequestPolyfill from "xhr4sw";
/**
* This is needed to polyfill as otherwise we get HttpRequest not defined
*/
Object.defineProperty(self, 'XMLHttpRequest', {
configurable: false,
enumerable: true,
writable: false,
value: XMLHttpRequestPolyfill
});
ここまでの作業を終わらせると、Cloudflare Workers / Pages functionsにデプロイしたHonoアプリでもMomentoにつなぐことができました。
コード全体像
参考までに、今回試したコードの全体像です。
import { Hono } from 'hono'
import {CacheGet, CacheClient, Configurations, CredentialProvider } from '@gomomento/sdk-web';
import XMLHttpRequestPolyfill from "xhr4sw";
/**
* This is needed to polyfill as otherwise we get HttpRequest not defined
*/
Object.defineProperty(self, 'XMLHttpRequest', {
configurable: false,
enumerable: true,
writable: false,
value: XMLHttpRequestPolyfill
});
const app = new Hono<{
Bindings: {
MOMENT_API_KEY: string
}
}>()
app.get('/', async c => {
const cacheClient = new CacheClient({
configuration: Configurations.Laptop.v1(),
credentialProvider: CredentialProvider.fromString({
apiKey: c.env.MOMENT_API_KEY,
}),
defaultTtlSeconds: 60,
});
await cacheClient.createCache('cache');
await cacheClient.set('cache', 'foo', 'FOO');
const getResponse = await cacheClient.get('cache', 'foo');
if (getResponse instanceof CacheGet.Hit) {
console.log(`Got value: ${getResponse.valueString()}`);
}
return c.json(getResponse.toString())
})
export default app
Q: Workers KV使えばいいのでは?
Hono + Wranglerでサンプルアプリを作ることが多いため、Cloudflare Workersで動かしたかったというのが今回の背景です。
実案件でまだMomentoを使ったことがないので、ユースケースは想像になりますが、「Cloudflare Workers以外からもデータにアクセスしたいケース」がある場合に出番があるかなとは思います。Workers KVは性質上Cloudflare Workersからアクセスする必要があります。一方Momentoはどこからでも、それこそ@gomomento/sdk-web
でクライアント(ブラウザ)から呼び出すこともできます。この辺りの特性が必要になる場合には、Momentoを検討するかなと思います。
あとは・・・Leaderboardなるプレビュー機能が2023/12時点である様子なので、これを触る場合もMomentoを選ぶかもしれません。