追記
Cloudflare Workers向けのWebフレームワークを作っているので、そちらを是非チェックしてみてください!
Cloudflare Workers が面白い。面白いので、いくつか簡単なアプリを作ってみた。例えば、そのひとつが Slack Bot で「yusukebe++
」とかやるとインクリメントされるやつ。
今回は Cloudflare Workers の面白さについて解説する。より興味のある方がいれば、上記のコードを参考にしてもらうといいだろう。
Cloudflare Workers とは?
Cloudflare の CDN エッジでスクリプトが動くのが Cloudflare Workers。いわゆる serverless の環境である。
追記
強調しておきたいのは、Cloudflare Wokers は serverless に加えて、「CDN のエッジで動くことを前提とした」環境であることだ。その目的と制限を理解しなくてはいけない。大きくて複雑なアプリは作れないし、作るべきではない。
JavaScript か Rust で書ける。他にも PHP と Kotlin とか Perl で書けると書いてあるが、Perl だとJS::inline
で JS 呼んでるだけだったので、まぁ基本 JS と Rust の 2 つと考えていいだろう。
Service Worker の API を踏襲してる。HTTP リクエストをハンドルするためのコードは以下の通りである。
async function handleRequest(request) {
return new Response("Hello Cloudflare Workers!!!!!!!!", {
headers: {
"content-type": "text/plain;charset=UTF-8",
},
});
}
addEventListener("fetch", (event) => {
return event.respondWith(handleRequest(event.request));
});
wrangler
という CLI ツールが提供されていて、これを使えば、テンプレートの生成、設定、ビルド、プレビュー、公開が全て手元でできてしまう。他にもminiflare
という Yet Another なツールもあったりして開発環境が充実している。
Workers KV
Workers KV という実にシンプルな Key-Value ストアが用意されている。
できるのはget/set/delete
相当のものだ。 TTL を設定でき、また値にメタデータを持たせることができる。それに加えて、list
という、指定した prefix を Key に持つ値をリストで取得するのがある。でもたったこれだけだ。
これだけだが、逆にシンプルなのがよい。Workers と統合されているため、スクリプトから呼び出しやすくて重宝する。
Workers Site
HTML や画像、CSS などファイルを静的にサーブする機能もある。Cloudflare には Pages という似たようなサービスがあるがそれとはまた別っぽい。
これを利用すれば、Hugo や Gatsby などの SSG や React や Vue などフロントエンドフレームワークをデプロイできる。
その他の機能
条件はあるものの、Cloudflare CDN のキャッシュのコントロールが可能だ。
他にも Websocket が使えたりする。
面白いところ
Cloudflare Workers で面白いと思った点を列挙する。
- 簡素。GCP や AWS でも severless のアーキテクチャはあるが、個人でパッと使うには複雑すぎる。管理画面もシンプルだ。
- デプロイまでがすごく楽。wrangler で Starter キットを生成して、
wranger publish
とすれば、ビルドをして、デプロイが完了する。自動でhttps://app-name.username.workers.dev
といった URL も発行してくれてる。
- Service Woker の API に寄せている件。ちなみに Deno deploy も Service Woker のそれを採用してて興味深い。
- Starter テンプレートを使えば、TypeScript で書く環境が一瞬で出来上がる。
- 最小限のオールインワン。Workers の基本機能で JSON を吐く API を作って、Workers Site にのせた SPA の React から呼び出す、とかをひとつのプロジェクトでできる。
- CDN と連動させれる。これから紹介するが、CDN のキャッシュをコントロールするために Workers が使える。
- 速い。実行時間が短く、何よりも CDN エッジで実行されるので、速い。
- 無料で使える。枠はあるが、個人で遊ぶ分には無料で済んでしまう。
- 当然ながら、node モジュールが使える。ただし、ビルドした JS が 1MB を超えるとデプロイできないので、ファットなモジュールは避けなくてはいけない。
例: Cloudflare Workers で作ったもの
例として、Cloudflare Wokers で作った 3 つのアプリを紹介する。
PC/Mobile の切り分け
以前からとあるサイトを Cloudflare の CDN にのせているのだが、ユーザーエージェントによって PC 版のページとモバイル版と出し分けをしたい。が、デバイスタイプによるキャッシュはEnterprise plans only
となっている。URL 以外をキャッシュキーに含めることができない。
そこで知恵を絞った結果、Workers でできた。以下の Wokers を CDN で動かす。キーを URL とデバイスタイプで生成する。
let isMobile = false;
const userAgent = request.headers.get("User-Agent") || "";
if (userAgent.match(/(iPhone|iPod|Android|Mobile)/)) {
if (!userAgent.match(/(iPad|Table)/)) {
isMobile = true;
}
}
const device = isMobile ? "Mobile" : "Desktop";
const cacheUrl = new URL(request.url + "-" + device);
const cacheKey = new Request(reqeust.url + device, request);
const cache = caches.default;
let response = await cache.match(cacheKey);
ついでに、レスポンスヘッダ s-maxge
に TTL を設定して、CDN にキャッシュする時間をスクリプトからコントロールした。
response.headers.append("Cache-Control", "s-maxage=" + ttl);
実用的だ。
Slack Bot
冒頭で紹介した Slack Bot である。
/karma yusukebe++
とするとyusukebe
という変数がインクリメントされる。逆に yusukebe--
はデクリメントだ。カルマの値はそれぞれ Workers KV にストアされていて、現在の数を返してくれる。
Key-Value ストア向きのアプリなので、何も考えずにできた。以下がKV_KARMA
という KV のオブジェクトに対してget
、put
を実行する。
const karma = async (name: string, operation: string) => {
const key = PREFIX + name;
const value = await KV_KARMA.get(key);
let karma = value != null ? parseInt(value) : 0;
if (operation == "++") {
karma = karma + 1;
} else {
karma = karma - 1;
}
await KV_KARMA.put(key, `${karma}`);
return `${name} : ${karma}`;
};
ブックマークアプリ
URL を渡すと OGP 付きで保存。リストで表示してくれるシンプルなブックマークアプリ。ブックマークレットや iOS のショートカットから登録できるようにしてて結構便利。
Workers で KV を利用した Web API を生やして、Workers Site で静的においた React の HTML、JS からそれを呼び出している。API => Frontend
という最近っぽい構成がひとつのプラットフォーム完結する、という例である。
まとめ
ちなみ Cloudflare、イケイケで
103 Early Hints
Signed Exchange
- AWS S3 対抗のストレージサービス「R2」
の機能のベータ提供・発表をここ 1 ヶ月以内に立て続けてにやってる。ブログも更新速いので興味深くみている。
ということで Cloudflare Wokers 面白いので、無料なので使ってみるといいだろう。今回は簡単なアプリしか作らなかったが、もっと頑張れるはずだ。さて、最後に断っておくが回し者ではないぞ!