はじめに
Better-T-Stackを利用して、Next.jsとHonoを組み合わせたモノレポ構成のアプリケーション環境を作る方法を説明していきます。
本記事で説明するアプリケーション環境は、フロント側にNext.jsのApp Router、バックエンド側にHonoフレームワークを使用し、フロントはVercelにバックエンドはCloudflareにデプロイします。モノレポ構成に使用するパッケージマネージャーはpnpm、ビルドツールはTurborepoを使用します。SupabaseやTursoといったデータベースとの連携については、本記事では取り扱いませんのでご注意ください。
Better-T-Stackとは
Better-T-Stackは、エンドツーエンドの型安全性(End-to-End Type-Safe)を備えたTypeScriptプロジェクトを、自分に必要な機能だけを選んで環境構築できる、最新のCLIツールです。
Philosophy
- Roll your own stack: pick only what you need, nothing extra.
- Minimal templates: bare-bones scaffolds with zero bloat.
- Latest dependencies: always current and stable by default.
- Free and open source: forever.
https://www.better-t-stack.dev/docs
引用にもあるように最新技術を使った型安全な環境を、肥大化させずにゼロから自由に組み立てられるツールと言えます。
次のコマンドで始めることができ、実行するとフロントやバックエンド、そしてDBや各種ランタイムの選択がターミナルを通して対話形式で選べます。
# Using bun (recommended)
bun create better-t-stack@latest
# Using pnpm
pnpm create better-t-stack@latest
# Using npm
npx create-better-t-stack@latest
良い点として、いちいちフレームワークとフレームワークの繋ぎこみや設定ファイルなどの作成を行わず、スピーディーにアプリの環境を構築できます。Webだけではなく、ネイティブアプリの環境作成にも対応しており、マルチプラットフォームの環境作りにも利用することができます。
難点として、Quick Startを実行しても、選択したフレームワークやオプションの組み合わせによってはビルドエラーが発生する場合があります。ゼロスタートはすごく早いのですが、意外にもその後に必要な加筆修正があるので、詰まりがちな修正点を説明して行きたいと思います。
Quick Start
次の環境で作成していきます。
- Select project type: Web
- frontend: Next.js
- backend hono
- runtime: Cloudflare Workers
- DB : None
- API Type : tRPC
- authentication provider : None
- addons : Turborepo, Biome
- Include Examples : None
- Web deployment: None
- Choose package Manager: pnpm
このコマンドを実行すると、上記の選択をスキップして実行できます。
pnpm create better-t-stack@latest my-app --frontend next --backend hono --runtime workers --database none --orm none --api trpc --auth none --payments none --addons turborepo biome --examples none --db-setup none --web-deploy none --server-deploy cloudflare --git --package-manager pnpm --install
作成したアプリのディレクトリに移動
cd my-app
生成されたディレクトリ構成
/
├── apps/
│ ├── server/ # Hono + Cloudflare Workers バックエンド
│ │ ├── src/
│ │ │ └── index.ts # Honoアプリ エントリーポイント
│ │ ├── .env
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── tsdown.config.ts
│ │
│ └── web/ # Next.js フロントエンド
│ ├── src/
│ │ ├── app/ # Next.js App Router
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ │ ├── components/ # UIコンポーネント
│ │ │ ├── ui/ # shadcn/ui コンポーネント
│ │ │ ├── header.tsx
│ │ │ ├── providers.tsx
│ │ │ └── theme-provider.tsx
│ │ ├── lib/ # ユーティリティ
│ │ └── utils/
│ │ └── trpc.ts # tRPCクライアント設定
│ ├── .env
│ ├── components.json
│ └── package.json
│
├── packages/
│ ├── api/ # tRPC API定義(共有)
│ │ ├── src/
│ │ │ ├── routers/ # tRPCルーター
│ │ │ ├── context.ts # tRPCコンテキスト
│ │ │ └── index.ts
│ │ └── package.json
│ │
│ ├── config/ # 共有設定
│ │ ├── tsconfig.base.json # 共通TypeScript設定
│ │ └── package.json
│ │
│ ├── env/ # 環境変数管理
│ │ ├── src/
│ │ │ ├── server.ts # サーバー用env(Cloudflare Workers)
│ │ │ └── web.ts # Web用env(t3-oss/env-nextjs)
│ │ ├── env.d.ts # Cloudflare環境の型定義
│ │ └── package.json
│ │
│ └── infra/ # インフラ設定(Alchemy)
│ ├── alchemy.run.ts # Cloudflare Workersデプロイ設定
│ ├── .env
│ └── package.json
│
├── biome.json # Biome(リンター/フォーマッター)設定
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml # pnpmワークスペース設定
├── tsconfig.json
└── turbo.json # Turborepoタスク設定
以上でBetter-T-StackのQuickStartは終了です。このままだと、ビルドエラーが起きるので次は修正点について説明していきます。
エラー修正
試しに、ビルドしてみましょう。Better-T-StackがV3.17.0時点の現在はエラーが出ると思います。
pnpm run build
../../packages/api/src/context.ts:1:45
Type error: Cannot find module 'hono' or its corresponding type declarations.
> 1 | import type { Context as HonoContext } from "hono";
| ^
2 |
3 | export type CreateContextOptions = {
4 | context: HonoContext;
packages/apiがHonoを依存関係として持っていないにもかかわらず、Honoをimportしてるため発生しています。
apps/serverがHonoを依存関係として持っているのですが、pnpm-workspace機能を利用して、一括管理しましょう。
1. pnpm-workspace.yamlにHonoを追加
"@trpc/client": ^11.7.2
"hono": "^4.8.2 # ← 追記
2. apps/server/package.jsonとpackages/api/package.jsonにhonoを追加する
"dependencies": {
"@hono/trpc-server": "^0.4.0",
"@trpc/server": "catalog:",
"@utsushimi-nikki/api": "workspace:*",
"@utsushimi-nikki/db": "workspace:*",
"@utsushimi-nikki/env": "workspace:*",
"dotenv": "catalog:",
"hono": "catalog:", // ← バージョン指定されているのを`catalog:`に変更
"zod": "catalog:"
}
"dependencies": {
"@trpc/client": "catalog:",
"@trpc/server": "catalog:",
"@types/pg": "catalog:",
"@utsushimi-nikki/db": "workspace:*",
"@utsushimi-nikki/env": "workspace:*",
"dotenv": "catalog:",
"drizzle-orm": "catalog:",
"zod": "catalog:",
"hono": "catalog:"
}
3. パッケージを更新してビルドを実行
pnpm install
先程のビルドエラーは出なくなっているはずです。
pnpm run build
Tasks: 2 successful, 2 total
Cached: 0 cached, 2 total
Time: 7.675s
以上で、エラー修正は終了です。
Alchemyの設定
Better-T-Stackはサーバー側のモジュールをCloudflareにデプロイするにあたりAlchemyを利用しています。初回設定は自分で行う必要があるので設定していきましょう。
Alchemyとは
Alchemy is a TypeScript library that creates and manages cloud infrastructure when you run it. Instead of using opinionated CLIs or configuration files, you write and run a regular TypeScript program.
https://alchemy.run/what-is-alchemy/
引用にあるように、TypeScriptでクラウドの管理・構築ができるライブラリです。一言で言えば、TypeScriptで記述するIaC(Infrastructure as Code)ツールです。
alchemy.run.tsといったファイルにインフラ定義を書き、実行するだけでリソースの作成・削除・更新ができます。
設定手順
こちらに記載されている手順を元にして行います。
Alchemyコマンドのスクリプト設定
Alchemy自体は、既にpackages/infraモジュールにインストールされていますが、このディレクトリでしかalchemyコマンドは実行できません。いちいち移動して実行するのが手間なので、ルートのpackage.jsonにscriptsを記載して楽にします。
"scripts": {
"alchemy": "pnpm -F @utsushimi-nikki/infra exec alchemy"
}
Alchemyのパスワードの変更
packages/infra以下の.envファイルに、Alchemyのパスワードが記載されています。各自決めた新しいパスワードを設定しましょう。
ALCHEMY_PASSWORD=please-change-this
Configure your Profile
pnpm run alchemy configure
┌ 🧪 Configure Profile
│
◇ Enter profile name
│ default
│
◇ Select a login method for Cloudflare
│ OAuth
│
◇ Customize scopes?
│ No
│
◇ Opening browser to authorize with Cloudflare...
│
│
└ ✅ Configured profile default
とりあえずでやるなら初回提示されている選択肢でいいと思います。
- Profile name : Alchemyが認証情報を保存する際に使用する名前
- login method for Cloudflare: Cloudflareに認証する方法
- Scope : デプロイ時に必要とする権限設定
OAuthの場合、URLがターミナルに表示されるのでクリックしてURL先で連携を承認してください。認証が成功すると以下の画面が表示されると思います。
Login to Cloudflare
すでにConfigure Profileでログインしているのでスキップでいいと思います。一応実行すると上書きするか聞いてくるのでNoを選択します。
pnpm run alchemy login
┌ 🧪 Login
│
◇ You are already logged in to cloudflare on profile "default".
│
◇ Would you like to overwrite?
│ No
└ Operation cancelled.
以上でAlchemyの初回設定は完了です。もし、パスワードを変更したい場合は.envで変更した後に.alchemyフォルダを全て削除してConfigure Profileからやり直してください。
サーバー側の起動スクリプト修正
Better-T-Stackが初回生成するサーバー起動のスクリプトがAlchemyを利用していないため、ルートpackage.jsonを修正します。
"scripts": {
"dev:server": "turbo -F server dev",
}
↓ 次に変更
"scripts": {
"dev:server": "turbo -F @my-app/infra dev"
}
修正したサーバースクリプトを実行し、サーバーを起動してください。http://localhost:3000 にアクセスしてOKの文字が表示されたら成功です。
また、webモジュールも同時に起動して確認しましょう。http://localhost:3001 にアクセスしてAPIStatusにConnectedが表示されれば問題ないです。
# Server
pnpm run dev:server
# Web+Server
pnpm run dev
デプロイ
環境別に読み込むenvファイルを変える
今のままだと、デプロイする際に使用するenvファイルが開発環境のままになってしまいます。新たに本番環境用のenvファイル.env.productionをapps/server以下に作成してください。値はWeb側をVercelにデプロイしない限り確定しないのでそのままで良いです。
CORS_ORIGIN=http://localhost:3001
次に、packages/infra/package.jsonのスクリプトを修正します。開発サーバーを起動するコマンドには.envをデプロイ・デストロイするコマンドには.env.productionを渡します。
"scripts": {
"dev": "alchemy dev --env-file ../../apps/server/.env",
"deploy": "alchemy deploy --env-file ../../apps/server/.env.production",
"destroy": "alchemy destroy --env-file ../../apps/server/.env.production"
}
最後にpackages/infra/alchemy.run.tsの不要なenvファイル読み込みを削除します。
config({ path: "./.env" });
config({ path: "../../apps/server/.env" }) // ← 削除
Alchemyによるデプロイ設定
まず、設定をする前にVercelのアクセストークンを取得してください。↓から取得できます。アカウントがない場合は新規作成してから行ってください。
取得したアクセストークンをpackages/infra/.envに追記してください。
VERCEL_ACCESS_TOKEN=取得したアクセストークン
次にpackages/infra/alchemy.run.tsにVercelのデプロイ設定を追加してください。
githubのアカウント名を控えるのと、アプリをgithubにpushしてリポジトリを作成している必要があります。
const app = await alchemy("my-app");
export const server = await Worker("server", {
// 省略
});
export const web = await Project("web", {
name: "vercelダッシュボードに表示されるアプリ名",
framework: "nextjs",
rootDirectory: "apps/web",
buildCommand: "pnpm run build",
installCommand: "pnpm install --frozen-lockfile",
environmentVariables: [
{
key: "NEXT_PUBLIC_SERVER_URL",
target: ["production", "preview"],
// biome-ignore lint/style/noNonNullAssertion: server.url is always defined after deployment
value: server.url!, // Cloudflare WorkerのURLを自動参照
},
],
gitRepository: {
type : "github",
repo : "githubのユーザ名/リモートリポジトリの名前"
}
});
設定を終えたら、デプロイを行いましょう。
pnpm run deploy
Alchemyによってバックエンド側はCloudflareにデプロイ出来たと思います。ターミナルに表示されたURLをクリックしてデプロイ出来ているか確認してください。OKが表示されていれば成功です。
Vercelにデプロイする
先程のデプロイ実行では不完全です。Vercelにコードのアップロードとビルドを行うには、プッシュするか既にpushするものがない場合はVercelのプロジェクトダッシュボードに行き、デプロイをする必要があります。
Vercelにアクセスしてダッシュボードから自分のアプリ名を探す
Developmentタブにアクセスし、Deploymentを作成する
ブランチはmainを選択して、Create Deploymentをクリック
Domain URLをコピーする
デプロイが出来たら、デプロイしたアプリのURLを取得しましょう。これは、バックエンド側がフロントとやり取りをする際に必要です。取得したら先程の.env.productionでそのまま変更していなかったCORS_ORIGIN_URLに設定します。
Overviewタブに行き、以下の画像にあるDomainsの部分にあるURLをコピーします。Deploymentの方でないので気をつけてください
# 環境変数を更新する
CORS_ORIGIN=Domains
バックエンド側の環境変数を更新するため、再度Alchemyを利用してデプロイします。
pnpm run deploy
以上でデプロイは終了です。最後にフロント側のURLにアクセスして以下のようにConnectedが表示されているなら、動作成功です。お疲れ様でした。
まとめ
本記事では、Better-T-Stackを利用してNext.jsとHonoを組み合わせたアプリケーションの構築方法を解説しました。DBの連携は行っていないですがBetter-T-StackはSupabaseやTursoといったBaaSも連携してくれるので試してみるといいかもしれません。
今回の技術スタックは無料で構築できるデプロイ環境なので、個人開発やとりあえずで動かすといった小さなアプリケーションで採用してみるのはありかもしれません。





