
エッジで輝く超高速ルーター!Cloudflare Workers × Honoで構築するAPI基盤の極意
こんにちは!Accel-Hack(アクセルハック) 開発チームです。
社内アシスタント「Airi」の開発記ブログも、今回で第6回目を迎えました。
前回のVol.5では、パッケージマネージャーに Bun を採用し、バックエンド(Cloudflare Workers)とフロントエンド(Vite / React)を単一のリポジトリで管理する「Bun Workspace」によるモノレポ構成についてご紹介しました。
この強固なモノレポ構造の上で、実際にバックエンドの Web API を構築し、外部サービス(Slack, freee, GitHub など)との通信を支えているのが、今回の主役である Hono(ほの) です。
Cloudflare Workers を用いたサーバーレス開発において、なぜ私たちが Hono を選定したのか、そして Airi における初期セットアップやルーティング構造について詳しく解説します。
1. 導入:エッジでの開発においてフレームワークに求めるもの
Cloudflare Workers は、V8 エンジン上で直接動作する非常に軽量で高速なサーバーレス環境です。
しかし、標準の Workers が提供する API(生のリクエスト/レスポンス処理)をそのまま利用して Web アプリケーションを構築しようとすると、すぐにルーティングやエラーハンドリング、ミドルウェアの適用などの「基礎的な部分」を自分たちで実装しなければならなくなります。
従来の Node.js 開発で使われていた Express などのフレームワークは、Node.js 固有の API に依存しているため、Cloudflare Workers などのエッジ環境ではそのまま動作しません。
また、動作したとしてもバンドルサイズが大きくなり、エッジの強みである「低遅延・高速起動(ゼロ・コールドスタート)」を損なってしまうという問題がありました。
そこで、私たちがバックエンドの Web API フレームワークに求めた要件は以下の3つでした。
超軽量であり、エッジ環境に最適化されていること
TypeScript ファーストで、安全に環境変数やコンテキスト変数を扱えること
Web Standards(Request / Response などの標準 Web API)に準拠していること
これらの要件を完璧に満たし、開発チームにとって「手になじむ」直感的な開発体験を提供してくれたのが、日本発のグローバルフレームワークである Hono でした。
2. なぜ「Hono」なのか? 選定の3つの決定打
① エッジネイティブ(超軽量・ゼロ依存)
Hono は、サードパーティ製ライブラリへの依存が「ゼロ(ゼロ依存)」で構築されています。
これにより、プロダクションビルド時のバンドルサイズが極めて小さく抑えられ、Cloudflare Workers の容量制限を圧迫しません。
また、独自の超高速ルーティングアルゴリズム(RegExpRouter)を採用しており、リクエストのパス解析にかかるオーバーヘッドがほぼ皆無です。
② TypeScript ファーストと型安全
Hono は TypeScript でフルスクラッチで書かれており、型定義が非常に充実しています。
例えば、Cloudflare Workers において最もバグが起きやすい「環境変数(Bindings)」の参照について、Hono はジェネリクスを介して型を縛ることができます。
これにより、開発中に型補完が利き、存在しない環境変数へのアクセスをコンパイル段階で検知できます。
③ Web Standards への準拠
Hono は、ブラウザや Service Worker で使われている標準の Web APIs(Request, Response, URLPattern など)をそのままベースにしています。
そのため、Cloudflare Workers に強くロックインされることなく、Deno, Bun, Vercel、さらには Node.js 環境など、他のあらゆる JavaScript ランタイムへの移植性が高いという安心感があります。
3. 実践:Airi における Hono の初期セットアップと構成
Airi のコードベースにおいて、Hono がどのように初期化され、リクエストをハンドリングしているか、実際のコード(index.ts)を基に紹介します。
リクエストルーティングの全体像
Airi におけるリクエストルーティングの全体フローは、以下のようになっています。

このように、エントリーポイントである index.ts から /slack のリクエストを SlackApp に逃がし、それ以外のリクエストを Hono アプリで受け取ったのち、各機能(Feature)のサブアプリへルーティングをディスパッチする構成をとっています。
エントリーポイントにおける「SlackApp」と「Hono」の棲み分け
Airi では、Slack 連携のインタラクション(Slash Command や Block Kit 画面の操作)を高速かつ安全に処理するために、専用の SlackApp(slack-edge-infra ベース)を使用しています。
一方で、Web UI や外部サービスの OAuth 認証コールバック、MCP サーバーのエンドポイントなどは Hono で処理します。
これらを綺麗に分離するため、Cloudflare Workers のエントリーポイント(fetch)でパス判定を行い、ディスパッチしています。
// index.ts より抜粋
export default {
async fetch(
request: Request,
env: AppEnv,
ctx: ExecutionContext,
): Promise<Response> {
appConfig.build(env);
const url = new URL(request.url);
// Slackからのリクエストは SlackApp で処理する
if (url.pathname.startsWith("/slack")) {
const slackApp = createSlackApp(env);
return await slackApp.run(request, ctx);
}
// それ以外の Web API / OAuth コールバックは Hono で処理する
return app.fetch(request, env, ctx);
},
// ...
};このように、Workers の生のエントリーポイントから app.fetch(request, env, ctx) を呼び出すだけで、Hono アプリに処理を委譲することができます。
ジェネリクスによる Bindings と型安全の担保
Hono のインスタンスを初期化する際、環境変数やコンテキスト変数の型をジェネリクスとして渡します。
const app = new Hono<{ Bindings: AppEnv; Variables: { userId: string } }>();Bindings: Cloudflare Workers の環境変数(KV データベース、D1、外部 API キーなど)の型 AppEnv を設定します。これにより、ハンドラー内で c.env.DB や c.env.SLACK_BOT_TOKEN のように、型安全に環境変数にアクセスできるようになります。
Variables: リクエストの生存期間中(ミドルウェアからハンドラーの間)で引き回すカスタム変数の型(例:認証済みの userId)を設定します。
app.route() による Feature-First アーキテクチャとの融合
前回の Vol.4 で紹介した「機能駆動(Feature-First)アーキテクチャ」では、各機能(OAuth、Web UI、MCP ツールなど)ごとにコードを独立させています。
Hono の app.route(path, subApp) 機能を活用することで、各機能(Feature)が独自に定義した Hono アプリを、メインのルーターへパズルのようにマウントすることができます。
app.route("/", webUiHandler.webUiApp);
app.route("/freee", freeeOauthHandler.freeeApp);
app.route("/github", githubOauthHandler.oauthApp);
app.route("/github", pullRequestHandler.webhookApp);
app.route("/google/mail", gmailHandler.gmailApp);
app.route("/mcp", mcpApp);このように定義することで、コードの凝集度が高まり、各機能のルーターを別個に開発・テストできるようになります。
4. 運用:エラーハンドリングとミドルウェアによる基盤の強化
Hono を採用したもう一つの大きなメリットが、堅牢なエラーハンドリングとミドルウェアの記述の簡潔さです。
堅牢なグローバルエラーハンドリング (app.onError)
API 開発において、予期せぬエラー(例外)を適切にキャッチし、ログに記録してユーザーに適切なレスポンスを返すことは必須です。
Hono では app.onError を使って一元管理できます。
app.onError(async (err, c) => {
console.error(`Unhandled error on ${c.req.method} ${c.req.url}:`, err);
await sendErrorAlert(`Unhandled error on ${c.req.method} ${c.req.url}`, err);
return c.text("Internal Server Error", 500);
});Airi では、例外が発生した際に sendErrorAlert を呼び出し、Slack の管理者用チャンネルへ即座にエラーのスタックトレースを通知する仕組みを導入しています。
これにより、本番環境で不具合が発生した際も、即時の検知と修正が可能になりました。
カスタムミドルウェアによるパフォーマンス監視
特定のリクエストの実行時間を自動的に測定し、ログを出力する独自のミドルウェアも、Hono なら数行で実装できます。
// APIの実行時間を測定しログに出力するミドルウェア
app.use("*", async (c, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
const seconds = ms / 1000;
console.log(
`[Execution Time] ${c.req.method} ${c.req.url} - ${seconds}s (${ms}ms)`,
);
});await next() を呼び出すことで、後続のルーティング処理やビジネスロジックを実行し、その前後のタイムスタンプの差分をとるだけで実行時間を測定できます。
5. 学びと成果:Hono がもたらした「手になじむ」開発体験
Airi のバックエンドに Hono を導入した結果、以下の素晴らしい成果が得られました。
コード量が劇的に削減された: Express や生の Workers API と比較して、直感的で美しい API デザイン(c.json(), c.text(), c.req.param() など)により、ボイラープレートコードが最小限になりました。
型安全によるバグの撲滅: Bindings が型安全になったことで、「本番環境だけで環境変数が読み込めず落ちる」といった、サーバーレス開発でありがちなトラブルを静的解析の段階で完全にシャットアウトできました。
エッジでの圧倒的なパフォーマンス: Hono の軽さと高速ルーティングにより、Airi の API レスポンスは常に数ミリ秒〜数十ミリ秒で返るため、極めてサクサク動くユーザー体験を実現しています。
6. おわりに
Hono の「軽量さ・型安全・Web Standards 準拠」という特徴は、Cloudflare Workers という強力な実行環境のパワーを最大限に引き出すために、最も適した選択肢でした。
これによって、Airi の Web API 基盤は非常に堅牢で、かつ拡張性の高いものとなっています。
次回は、この Hono アプリを動かし、KV データベースや D1、AI Gateway といった各種リソースを統合して管理する、「Vol. 7: サーバーレス・インフラの構築「Cloudflare」」 についてご紹介します!
Cloudflare の強力なエコシステムをどのように活用しているのか、その全貌をお届けしますので、お楽しみに!
この記事は以下の体制で作成されました。
ドラフト作成・執筆: Airi (社内AIアシスタント)
アイデア出し・レビュー: CTO
デザイン: 事務スタッフ
Accel-Hackでは、テクノロジーを駆使して世の中の課題を解決していく仲間を募集しています。
私たちの取り組みに少しでも興味を持っていただけたら、ぜひ採用ページや会社サイトも覗いてみてくださいね。