Hono を利用しているプロジェクトで Cloudflare Workers の Cron Triggers を設定する
どうも、torahack こと稲垣です。
私は最近、「マネジメント:開発 = 7:3」くらいの業務比率で、エンジニアリングマネージャー(?)に近い動きをしています。
開発プロジェクト初期段階に技術選定をしてコードベースを作成するのですが、最近は Cloudflare に注目しています。そして Cloudflare を基盤とするプロジェクトで API 等サーバーサイドの開発をする場合、フレームワークとして Hono を利用している方は多いでしょう。(多少の偏見および思想が入っている。 Hono 最高!という話はまた別の記事でしたい所存)
Hono を利用しているプロジェクトでサーバーサイドの開発を進める中で「定期実行したい処理はどう実現しようか」という問題にぶつかりました。
結論としてたどり着いたのは Cloudflare Workers が提供している Cron Triggers を利用することです。
Cron Triggers とは
Cron Triggers では、scheduled() handler を用いて Workers をスケジュール実行できます。
簡易的なバッチ処理を実装するのに適していると思います。
( e.g. 外部API からデータ取得して D1 のデータベース に同期するなど)
Cron Triggers の一般的な設定方法
公式ドキュメントを参照して Workers に scheduled() handler を定義します。
※以下のサンプルコードは公式ドキュメントと同等です。
const handler: ExportedHandler = {
async scheduled(event, env, ctx) {
console.log("cron processed");
},
};
export default handler;
Cron Triggers という名前の通り、一般的な cron 式でスケジュールを定義できます。
Workers を Wrangler で管理している場合、 wrangler.toml に定義してください。
[triggers]
# Schedule cron triggers:
# - At every 3rd minute
# - At 3PM on first day of the month
# - At 11:59PM on the last weekday of the month
crons = [ "*/3 * * * *", "0 15 1 * *", "59 23 LW * *" ]
なお、ダッシュボード上でも Cron Triggers を設定できる UI が用意されています。
ちなみに、1つの Workers に複数の Cron Triggers を定義できます。cron 式で分岐させることになるようです。
const handler: ExportedHandler = {
async scheduled(event, env, ctx) {
// Write code for updating your API
switch (event.cron) {
case "*/3 * * * *":
// Every three minutes
await updateAPI();
break;
case "*/10 * * * *":
// Every ten minutes
await updateAPI2();
break;
case "*/45 * * * *":
// Every forty-five minutes
await updateAPI3();
break;
}
console.log("cron processed");
},
};
export default handler;
ローカル環境でもテスト実行できます。
npx wrangler dev --test-scheduled
curl "http://localhost:8787/__scheduled?cron=*+*+*+*+*"
Hono を利用しているプロジェクトに Cron Triggers を統合する
本題&結論を書きます。
Cloudflare Workers のランタイムで Hono を利用する場合、一般的にはこう書きますね。
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello Cloudflare Workers!'))
export default app
同じプロジェクト内で Cron Triggers を設定したい場合、こう書けます。
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello Cloudflare Workers!'))
const scheduled: ExportedHandlerScheduledHandler = async (event, env, ctx) => {
ctx.waitUntil(doSomeTaskOnASchedule())
}
export default {
fetch: app.fetch,
scheduled,
}
この実装方法は以下の記事を参考にしました。
Hono は Cloudflare Workers で扱える handler のうち fetch handler を提供しています。なので以下は同等ということだそうです。
- export default app
+ export default {
+ fetch: app.fetch
+ }
同様の仕様で、Email handler なども統合できるのかなーと思います。
さて、以降はこの実装方法に対して私が感じるメリットと、そのメリットを活かした具体例を紹介します。
Hono プロジェクトに Cron Triggers を統合できると何が嬉しいのか?
私は Hono + Cloudflare D1 + Prisma + Kysely という構成で API を構築しています。
Cron Triggers として定期実行したい関数内で、 Hono で作る API と同様にデータベース操作ができることが嬉しいです。当然、Schema から生成された型定義やクエリも参照できます。
この実装方法を知る前は、別リポジトリを立てたりモノレポにしようと試みたのですが、上述した型定義やクエリを共有するのが面倒でした。
実際に Hono プロジェクトに Cron Triggers を統合した例
私は Cron Triggers でスケジュール実行する関数内で、GCP の OAuth 2.0 クライアントで外部 API をコールして、取得したデータを Cloudflare D1 に保存するということをしました。
OAuth 2.0 クライアントの秘匿情報は wrangler.toml 内に環境変数として定義しています。
[vars]
OAUTH_CLIENT_ID = "YOUR_OAUTH_CLIENT_ID"
OAUTH_CLIENT_SECRET = "YOUR_OAUTH_CLIENT_SECRET"
REFRESH_TOKEN = "YOUR_REFRESH_TOKEN"
以下のようにEnv
型に環境変数を定義して、ExportedHandlerScheduledHandler<T>
のジェネリクスに渡すことで、型安全にenv
パラメータにアクセスできます。
また、DB: D1Database
も環境変数として渡しているので、Cron Triggers として定期実行したい関数内で prisma-kysely のクライアントを利用することができます。
import { Hono } from 'hono'
type Env = {
DB: D1Database
OAUTH_CLIENT_ID: string
OAUTH_CLIENT_SECRET: string
REFRESH_TOKEN: string
}
const app = new Hono<{ Bindings: Env }>()
// `ExportedHandlerScheduledHandler<T>`のジェネリクスに`Env`型を指定する
// スケジュールされたハンドラー関数内で`env`パラメータを使用して、`Env`型で定義された環境変数に型安全にアクセスできます。
const scheduled: ExportedHandlerScheduledHandler<Env> = async (event, env, ctx) => {
ctx.waitUntil(callExternalApiByOauthClient(env))
}
export default {
fetch: app.fetch,
scheduled,
}
まとめ
Hono を利用しているプロジェクトで Cloudflare Workers の fetch handler だけでなく scheduled handler も利用する方法をご紹介しました。
最後に、toraco株式会社では2024年11月1日にエンジニア向けのコミュニティを立ち上げました。
Discord のサーバーで運営しており、以下のリンクから無料で参加できます。コミュニティ内では以下のような投稿・活動がされます!
Discussion