Zenn
🔥

React Router v7 + Hono + bun でモノレポ構成の初期構築

2025/03/31に公開
2

この記事は?

この記事では、bunworkspaces 機能を利用してモノレポ構成を構築し、バックエンドに Hono、フロントエンドに React Router v7を採用した場合の初期設定手順です。

バックエンドとフロントエンド間の通信には Hono の RPC (Remote Procedure Call) 機能を利用し、型安全な API コールを実現します。
deploy先として、Cloudflare Workersを想定しています。

インストール

プロジェクトのセットアップを行います。

1. 作業ディレクトリの作成とGit初期化

mkdir sample-app
cd sample-app
git init

2. .gitignore の作成

node_modules を無視するように設定します。

.gitignore
node_modules

3. ワークスペース用ディレクトリの作成

モノレポの各アプリケーション(今回はバックエンドとフロントエンド)を配置するディレクトリを作成します。

mkdir apps

4. Bunのバージョンの確認(参考)

この記事の手順は以下のバージョンで確認しました。

node -v
# v22.2.0
bun -v
# 1.1.42

※Bunがインストールされていない場合は、Bun公式サイト の手順に従ってインストールしてください。

5. ルート package.json の作成

モノレポのルートに package.json を作成し、workspaces を設定します。"private": true は、ルートパッケージ自体が公開されることを防ぐためのお約束です。

package.json
{
  "name": "sample-app",
  "version": "1.0.0",
  "private": true,
  "workspaces": [
    "apps/*"
  ]
}

6. バックエンド (Hono) の作成

apps ディレクトリに移動し、bun create コマンドで Hono アプリケーションを作成します。

bun create hono@latest backend

templateはcloudflare-workersを選択します。

Which template do you want to use? › cloudflare-workers
Do you want to install project dependencies? › Yes
Which package manager do you want to use? › bun

7. フロントエンド (React Router) の作成

同様に apps ディレクトリ内で、bunx create-react-router コマンドを使って React Router アプリケーションを作成します。テンプレートには Cloudflare 向けのものを利用します。

bunx create-react-router@latest --template remix-run/react-router-templates/cloudflare frontend

Gitリポジトリはルートを利用するため No を選択します。

Initialize a new git repository? › No
Install dependencies with bun? › Yes

8. フロントエンドからバックエンドへの依存関係追加

フロントエンドからバックエンドの型定義などを参照するために、frontend アプリケーションに backend ワークスペースと hono を追加します。

cd frontend
bun add hono backend

これで、基本的なファイル構造と依存関係のインストールが完了しました。

backend設定

次に、apps/backend の設定を行います。

1. package.json の変更

package.json を以下のように設定します。

apps/backend/package.json
{
  "name": "backend", // 後で変更します
  "type": "module",
  "scripts": {
    "dev": "wrangler dev",
    "deploy": "wrangler deploy --minify"
  },
  "exports": {
    ".": "./src/index.ts"
  },
  "dependencies": {
    "hono": "^4.7.5"
  },
  "devDependencies": {
    "@cloudflare/workers-types": "^4.20250214.0",
    "wrangler": "^4.4.0"
  }
}

【重要】 後述する開発サーバー起動の問題のため、この name は後で @sample-app/backend に変更します。

2. src/index.ts の編集

API エンドポイントを作成します。今回はインストール時に用意されている /hello という GET エンドポイントをそのまま利用します。また、ローカル開発時にフロントエンド (localhost:5173) からのアクセスを許可するために CORS ミドルウェアを設定します。
最後に、export type AppType = typeof route; で Hono アプリケーションの型情報をエクスポートします。これが Hono RPC のキーとなります。

apps/backend/src/index.ts
import { Hono } from "hono";
import { cors } from "hono/cors";

const app = new Hono();

// すべてのオリジンからのアクセスを許可(開発用)
app.use("*", cors({
  origin: "*"
}));

// /hello エンドポイント
const route = app.get("/hello", (c) => {
  return c.json({ message: "Hello Hono!" })
});

// Hono Client (RPC) のために型をエクスポート
export type AppType = typeof route;
export default app;

frontend設定

次に、apps/frontend の設定を行います。

1. Hono Client の設定ファイル作成

バックエンド API と通信するためのクライアントを設定します。hono/clienthc を利用します。

apps/frontend/app/lib/client.ts を作成します。

apps/frontend/app/lib/client.ts
import type { AppType } from "backend"; // 後で変更します
import { hc } from "hono/client";

// TODO: URLはいったんローカルを固定で記述
export const client = hc<AppType>("http://localhost:8787"); // wrangler dev のデフォルトポート

ここで import type { AppType } from "backend"; と記述することで、backend ワークスペースから型定義をインポートしています。これにより、client はバックエンドのエンドポイント (/hello) の型情報を認識し、型安全なコーディングが可能になります。

【重要】 後述する開発サーバー起動の問題のため、この import パスは後で @sample-app/backend に変更します。

2. ルートコンポーネントの編集

React Router のルート (/) に対応するコンポーネントを編集し、先ほど作成した Hono Client を使ってバックエンドからデータを取得・表示するようにします。React Router v7 では、loader 関数内でデータの取得を行います。

apps/frontend/app/routes/home.tsx を以下のように編集します。

apps/frontend/app/routes/home.tsx
import type { Route } from "./+types/home";
import { client } from "~/lib/client"; // 作成した client をインポート

export function meta({}: Route.MetaArgs) {
  return [
    { title: "New React Router App" },
    { name: "description", content: "Welcome to React Router!" },
  ];
}

// loader 関数でバックエンドからデータを取得
export async function loader({ context }: Route.LoaderArgs) {
  // Hono Client を使って /hello エンドポイントを呼び出す (型安全!)
  const res = await client.hello.$get();
  const hello = await res.json();
  return {
    hello: hello.message // { hello: "Hello Hono!" } というオブジェクトを返す
  };
}

// コンポーネントは loaderData からデータを受け取る
export default function Home({ loaderData }: Route.ComponentProps) {
  return (
      <div>
        {/* loader から渡されたデータを表示 */}
        {loaderData.hello}
      </div>
  )
}

client.hello.$get() のように、型情報に基づいてメソッドチェーンで API を呼び出せるのが Hono Client の特徴です。

3. 開発サーバーの起動と問題解決

ルートディレクトリに戻り、bun run --filter コマンドで両方の開発サーバーを同時に起動しようとします。

cd ../.. # ルートディレクトリへ移動
bun run --filter '*' dev

しかし、この時点では backendwrangler dev は起動するものの、frontend の開発サーバーが waiting for ... のような状態で止まってしまい、正常に起動しませんでした。

調査したところ、以下の Issue が関連している可能性がありました。
https://github.com/oven-sh/bun/issues/10386

apps/backend/package.jsonapps/frontend/package.jsonname フィールドを @<scope>/<package-name> の形式(スコープ付きパッケージ名)に変更することで解決しました。
(根本的な原因は完全には特定できていませんが、スコープ付きパッケージ名にすることで Bun のワークスペース解決がうまくいく?)

apps/backend/package.json
{
  "name": "@sample-app/backend", // 変更
  // ... other fields
}
apps/frontend/package.json
{
  "name": "@sample-app/frontend", // 変更
  // ... other fields
}

この変更に伴い、frontendbackend の型をインポートしている箇所のパスも変更する必要があります。

apps/frontend/app/lib/client.ts
import type { AppType } from "@sample-app/backend"; // パスを変更
import { hc } from "hono/client";

// TODO: URLはいったんローカルを固定で記述
export const client = hc<AppType>("http://localhost:8787");

これらの変更後、再度 bun run --filter '*' dev を実行すると、バックエンドとフロントエンドの両方の開発サーバーが正常に起動しました。

ブラウザで http://localhost:5173/ にアクセスすると、バックエンドから取得した "Hello Hono!" が表示されるはずです。

4. API URL の環境変数化 (TODO解消)

apps/frontend/app/lib/client.ts にハードコードされていたバックエンドの URL (http://localhost:8787) を環境変数から読み込むように修正します。

まず、.env ファイルが Git 管理されないように、apps/frontendの .gitignore に追記します。

apps/frontend/.gitignore
.env # 追記

.env ファイルを作成します。React Router (Vite) テンプレートでは、VITE_ プレフィックスが付いた環境変数がクライアントサイドコードに公開されます。

.env
VITE_API_URL=http://localhost:8787

最後に、client.ts を修正して環境変数を読み込みます。

apps/frontend/app/lib/client.ts
import type { AppType } from "@sample-app/backend";
import { hc } from "hono/client";

// import.meta.env から環境変数を読み込む
export const client = hc<AppType>(import.meta.env.VITE_API_URL);

これで、開発環境では .env ファイルの値が使われ、デプロイ時にはデプロイ環境の環境変数を設定することで、URL を切り替えられるようになります。

まとめ

この記事では、Bun Workspaces を利用してモノレポ環境を構築し、バックエンドに Hono、フロントエンドに React Router v7 をセットアップする初期手順を解説しました。

  • bun createbunx create-react-router で各アプリケーションを初期化
  • ルートの package.jsonworkspaces を設定
  • Hono で API を作成し、AppType をエクスポート
  • React Router の loader で Hono Client (hc) を使用し、型安全な API 通信を実装
  • 開発サーバー同時起動時の問題と、スコープ付きパッケージ名への変更による対処法
  • API エンドポイントの URL を環境変数で管理する方法

Hono の RPC 機能により、バックエンドとフロントエンド間の連携が非常にスムーズかつ安全に行えることが確認できました。Bun を活用したモノレポ構成の一例として参考になれば幸いです。

GitHub

本記事で作成したコードは以下のリポジトリで公開しています。

参考記事

2

Discussion

ログインするとコメントできます