こんにちは。
この記事は UEC Advent Calendar 2025 の 4 日目の記事です。
昨日の記事は
id:ushiromiya3 さんの 「天井画や壁画はあるのに床画はないよね」でした。
自分は学部卒予定というのもあり 修士 / 博士後期課程への解像度が低いので、その世界が垣間見えて興味深かったです。
さて、本題に入ります。 私は普段 TypeScript で Web アプリケーションを書くことが多く、 バックエンドには Hono・フロントエンドには React Router をよく使っています。
Hono - Web framework built on Web Standards
React Router Official Documentation
Hono と React Router はどちらも Web 標準の Fetch API に準拠したランタイムモデルを採用しているため、Hono アプリケーションから React Router アプリケーションへ処理を移譲することができます。
この記事では、その具体的な方法を切り口に、Web Fetch API に準拠した JavaScript Web フレームワーク同士をつなぎ合わせることやその実用性について考えてみたいと思います。
Hono から React Router へ処理を移譲する方法
React Router には createRequestHandler() という API があり、
サーバーサイドのビルド成果物を渡すと次のようなシグネチャの関数を生成します。
( ※ このシグネチャは簡略化されたものです )
(req: Request) => Promise<Response>
なんだか fetch() に似たシグネチャですね。
つまり、 createRequestHandler はフレームワークのサーバーサイドの処理を Web Fetch API レベルに抽象化しているといえそうです。
これにより、バックエンドロジックを記述したアプリケーションに React Router をつないでひとつのアプリケーションとしてデプロイできるようになります。
例えば Hono を使っている場合、このようなコードを書くだけでよいです。
import { createRequestHandler } from "react-router"; import { Hono } from "hono"; const reactRouterHandler = createRequestHandler( () => import("./build/server/index.js") ); const app = new Hono(); // ここに通常の Hono バックエンドロジックを書く // マッチしなかったリクエストは React Router に委譲する app.all("*", async (c) => { return reactRouterHandler(c.req.raw); }); export default app;
フレームワーク同士をつないで適材適所で処理を書く
JavaScript の Web フレームワークは基本的にフロントエンドかサーバーサイドのどちらか片方を主軸とするものが多いです。*1
フロントエンドフレームワークは「各ページで UI を高速に描画しユーザーと効率良く堅牢にインタラクションする」方向に進化してきました。 特にルーティングや Request / Response はフレームワークが抽象化しており、内部で様々な最適化が行われています。
そのため、Request / Response を直接触るような Middleware 的な処理を書こうとするとフレームワーク独自の作法に従う必要があります。
例えば Next.js では proxy というしくみが用意されていますね。
一方でサーバーサイドフレームワークで Request / Response を触れるのは、生い立ちからして当然です。
そのため Middleware 的な処理も、素直なコードで記述できます。
Hono と React Router をつないだ場合、Middleware 的な処理は Hono のレイヤで処理することが可能になります。 すべてのページに BASIC 認証をかけたい場合も、これだけで済むはずです。
import { createRequestHandler } from "react-router"; import { Hono } from "hono"; import { basicAuth } from "hono/basic-auth"; const reactRouterHandler = createRequestHandler( () => import("./build/server/index.js"), ); const app = new Hono(); app.all( "*", // basicAuth Middleware を React Router に処理を移譲する前に実行している basicAuth({ username: "admin", password: "password" }), async (c) => { return reactRouterHandler(c.req.raw); } ); export default app;
このように、得意分野が異なるフレームワークをつなぎ合わせることで、行いたい処理をメンタルモデルに合った箇所に記述できるようになります。
新たに発生する課題
ここまで、フレームワークを Web Fetch API レベルでつなぐ利点を説明してきました。
しかし、実際には Web Fetch API レベルに抽象化されたフレームワーク同士をつなぐだけでは、アプリケーションを運用するには不十分です。
先述したような Hono と React Router をつなぐ例で、ユーザー認証のあるアプリケーションを作る場合を考えてみます。
ログインが必要なページでは、例えば次のような順番で処理を進めたいはずです。
- Hono 側で Cookie などを検証してユーザーの認可・認証を行う
- 認可・認証を通過した場合のみ React Router を呼び出す
- React Router 側のページコンポーネントで、ユーザー情報が必要な UI を描画する
このとき、React Router 側でユーザーの情報を取り出すために、もう一度 Cookie を検証しないといけなかったら面倒です。
理想的には、Hono から React Router にユーザー情報を渡して、React Router 側では直接値にアクセスできたほうがよいでしょう。
つまりフレームワークをつないで実用的なアプリケーションを作る上では、 フレームワーク間で JavaScript の値をやりとりできる 必要性がありそうです。
Hono から React Router に JavaScript の値を渡す
Hono と React Router をつなぐ場合、このような要求に応えることが可能です。
冒頭で紹介した React Router の createRequestHandler() で生成される関数のシグネチャは、ここまで簡略化した形で書いていましたが、実際にはこのようになっています。
( request: Request, loadContext?: MiddlewareEnabled extends true ? RouterContextProvider : AppLoadContext, ) => Promise<Response>;
今回は MiddlewareEnabled が true の場合に絞って説明しますが、 RouterContextProvider というのは React Router v8 で Middleware が導入されるのに合わせて追加される予定の概念です。
React Router における RouterContext というのは loader や Middleware などのサーバーサイドの処理の間で値を共有するためのしくみで、RouterContextProvider は各 RouterContext を束ねて管理しています。
createRequestHandler()で生成した関数の第 2 引数に RouterContextProvider を渡せるということは、Hono 側から JavaScript の値を RouterContext として React Router に渡せそうですね。
ということでそれを実際に試した結果を GitHub で公開しています。 この例は実際のアプリケーションとやや遠いものの、応用すると Hono 側で認証したユーザー情報を React Router に渡すなどが可能です。 プライベートで作っているアプリケーションではこの構成を試していますが、一定程度実用的だと感じています。
まとめ
Web Fetch API レベルで JavaScript の Web フレームワークをつなぐのは実用的なのか、という問いについて、Hono と React Router を例に考えてみました。
個人開発の規模感では、この構成は自分のメンタルモデルにかなり合っていて、書いていて違和感がありません。 バックエンドは Hono、フロントエンドは React Router と、各フレームワークが得意とする分野に注力できるのが気に入っている点です。
とはいえ、これはあくまでプライベートの開発規模での話であり、実際に業務レベルのプロダクトで使った場合、フレームワーク間のデバッグやログ収集といった運用まわりの課題が発生するはずです。
それでも、Web 標準のインターフェイスでフレームワークをまたいで処理を接続できるのは新鮮な体験で、触っていて楽しいと感じました。 今後もこういった土管細工を楽しんでいきたいですね。
最後に
UEC Advent Calendar 2025 の明日の記事はちゅまよさんの「 DIY望遠鏡 Hadley telescopeを作った話」です。 お楽しみに!
おわり
おわり