BuildCha は 3D 表現と AI を組み合わせた街づくり支援アプリケーションのモノレポです。Cloudflare Workers 上で動作する Hono ベースのバックエンドと、Next.js 15 / React 19 のフロントエンドを単一リポジトリで管理しています。
apps/backend— LangChain・Prisma・Better Auth などを用いた Cloudflare Workers APIapps/frontend— 3D 表現を行う Next.js 15 (Turbopack) フロントエンド.devcontainer— VS Code Dev Container 設定 (Node.js 22 + pnpm)compose.yml— Docker Compose によるローカルプレビュー設定- そのほか、Biome による整形/静的解析や Husky の Git Hook 設定をルートで管理しています
- Node.js 22 系 (推奨は Dev Container と同じバージョン)
- pnpm 10.11.0 (
package.jsonのpackageManagerで固定) - Cloudflare アカウントと
wranglerCLI (バックエンドのデプロイ・ローカル実行に必須) - Docker / Docker Compose (Dev Container やコンテナ実行を利用する場合)
プロジェクトルートで依存関係をインストールします。
pnpm install初回のみ Cloudflare へのログインとローカル DB 用マイグレーション・Prisma クライアント生成を行ってください。
pnpm --filter backend wrangler login
pnpm --filter backend local:migration
pnpm --filter backend build:prisma- バックエンド:
apps/backend/.env.exampleをコピーして.envを作成します。OPENAI_API_KEY— OpenAI の API キーUSE_OPENAI_MODEL_NAME— 既定値 (gpt-4o-miniなど) を指定BETTER_AUTH_SECRET— Better Auth のドキュメントで生成したシークレットBETTER_AUTH_URL— ローカル開発時はhttp://localhost:8787
- フロントエンド: API エンドポイントを切り替える場合は
apps/frontend/.env.localを作成し、NEXT_PUBLIC_RPC_URLを設定します (未設定時はhttp://localhost:8787を自動使用します)。
Cloudflare D1 / Vectorize などのバインディングは apps/backend/wrangler.jsonc で定義されています。必要に応じて Cloudflare ダッシュボード側のリソースを用意してください。
バックエンドとフロントエンドをそれぞれ別ターミナルで起動するのが推奨です。
pnpm dev:back # = pnpm --filter backend dev- ポート:
http://localhost:8787 - Prisma スキーマを変更した際は
pnpm --filter backend build:prismaを再実行してください。 - D1 のスキーマ変更は
pnpm --filter backend local:migrationで適用できます。 - prismaのスキーマを変更した場合は
npx prisma migrate diff --from-local-d1 --to-schema-datamodel ./prisma/schema.prisma --script --output migrations/名前.sqlでマイグレーションファイルを生成し、apps/backend/migrationsに追加してください。
pnpm dev:front # = pnpm --filter frontend dev- ポート:
http://localhost:3000 - Turbopack が有効なため、差分に追従した高速なホットリロードが利用できます。
pnpm devはバックエンド→フロントエンドの順に逐次実行するスクリプトのため、同時起動には別ターミナルでdev:back/dev:frontを実行してください。
- バックエンド:
pnpm build:backで TypeScript ビルドと Prisma 生成、pnpm deploy:backで Cloudflare Workers にデプロイ - フロントエンド:
pnpm build:frontで本番ビルド、pnpm deploy:front/pnpm upload:frontで OpenNext (Cloudflare Pages/Workers) への反映 - 必要に応じて
pnpm deployでバックエンド→フロントエンドの順にデプロイできます
pnpm lint— Biome による lintpnpm format— Biome によるコード整形pnpm check— Biome の型・Lint チェックpnpm --filter backend test— バックエンドの Jest テスト (モック環境変数はapps/backend/test/__mocks__/cloudflareWorkersMock.tsを参照)
- VS Code に Dev Containers 拡張と Docker Desktop をインストール
- リポジトリを開き、パレットで
Dev Containers: Reopen in Containerを実行 - コンテナ起動後、自動で
pnpm installが実行されます - コンテナ内ターミナルで
pnpm dev:back/pnpm dev:frontを利用してください
フォルダー構造
.
├── README.md
├── apps
│ ├── backend
│ │ ├── README.md
│ │ ├── dist
│ │ │ ├── ai
│ │ │ │ ├── chatBot.d.ts
│ │ │ │ ├── chatBot.js
│ │ │ │ ├── compareImages.d.ts
│ │ │ │ ├── compareImages.js
│ │ │ │ ├── create3DObject.d.ts
│ │ │ │ ├── create3DObject.js
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ ├── roofAlignmentTool.d.ts
│ │ │ │ ├── roofAlignmentTool.js
│ │ │ │ ├── schemas.d.ts
│ │ │ │ ├── schemas.js
│ │ │ │ └── tools
│ │ │ │ ├── githubTools.d.ts
│ │ │ │ ├── githubTools.js
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ ├── vectorSearchTool.d.ts
│ │ │ │ └── vectorSearchTool.js
│ │ │ ├── app.d.ts
│ │ │ ├── app.js
│ │ │ ├── client.d.ts
│ │ │ ├── client.js
│ │ │ ├── config.d.ts
│ │ │ ├── config.js
│ │ │ ├── lib
│ │ │ │ ├── auth.d.ts
│ │ │ │ ├── auth.js
│ │ │ │ ├── githubMcpClient.d.ts
│ │ │ │ ├── githubMcpClient.js
│ │ │ │ ├── prisma.d.ts
│ │ │ │ └── prisma.js
│ │ │ ├── map.d.ts
│ │ │ ├── map.js
│ │ │ ├── moc
│ │ │ │ ├── getAnswerObject.d.ts
│ │ │ │ └── getAnswerObject.js
│ │ │ ├── prisma
│ │ │ │ ├── schemas.d.ts
│ │ │ │ └── schemas.js
│ │ │ ├── quest.d.ts
│ │ │ ├── quest.js
│ │ │ ├── routes
│ │ │ │ ├── map.d.ts
│ │ │ │ ├── map.js
│ │ │ │ ├── object.d.ts
│ │ │ │ ├── object.js
│ │ │ │ ├── quest.d.ts
│ │ │ │ ├── quest.js
│ │ │ │ ├── r2.d.ts
│ │ │ │ ├── r2.js
│ │ │ │ ├── user.d.ts
│ │ │ │ └── user.js
│ │ │ ├── user.d.ts
│ │ │ └── user.js
│ │ ├── generated
│ │ │ └── prisma
│ │ │ ├── client.d.ts
│ │ │ ├── client.js
│ │ │ ├── default.d.ts
│ │ │ ├── default.js
│ │ │ ├── edge.d.ts
│ │ │ ├── edge.js
│ │ │ ├── index-browser.js
│ │ │ ├── index.d.ts
│ │ │ ├── index.js
│ │ │ ├── libquery_engine-darwin-arm64.dylib.node
│ │ │ ├── libquery_engine-linux-arm64-openssl-3.0.x.so.node
│ │ │ ├── libquery_engine-linux-musl-arm64-openssl-3.0.x.so.node
│ │ │ ├── package.json
│ │ │ ├── query_engine_bg.js
│ │ │ ├── query_engine_bg.wasm
│ │ │ ├── runtime
│ │ │ │ ├── edge-esm.js
│ │ │ │ ├── edge.js
│ │ │ │ ├── index-browser.d.ts
│ │ │ │ ├── index-browser.js
│ │ │ │ ├── library.d.ts
│ │ │ │ ├── library.js
│ │ │ │ ├── react-native.js
│ │ │ │ ├── wasm-compiler-edge.js
│ │ │ │ └── wasm-engine-edge.js
│ │ │ ├── schema.prisma
│ │ │ ├── wasm-edge-light-loader.mjs
│ │ │ ├── wasm-worker-loader.mjs
│ │ │ ├── wasm.d.ts
│ │ │ └── wasm.js
│ │ ├── jest.config.js
│ │ ├── migrations
│ │ │ ├── 0001_initial.sql
│ │ │ ├── 0002_create_tables.sql
│ │ │ ├── 0003_update_table.sql
│ │ │ ├── 0004_update_table.sql
│ │ │ └── 0005_update_object.sql
│ │ ├── package.json
│ │ ├── prisma
│ │ │ └── schema.prisma
│ │ ├── src
│ │ │ ├── ai
│ │ │ │ ├── chatBot.ts
│ │ │ │ ├── compareImages.ts
│ │ │ │ ├── create3DObject.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── schemas.ts
│ │ │ │ └── tools
│ │ │ │ ├── githubTools.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── vectorSearchTool.ts
│ │ │ ├── app.ts
│ │ │ ├── client.ts
│ │ │ ├── config.ts
│ │ │ ├── lib
│ │ │ │ ├── auth.ts
│ │ │ │ └── prisma.ts
│ │ │ ├── prisma
│ │ │ │ └── schemas.ts
│ │ │ └── routes
│ │ │ ├── map.ts
│ │ │ ├── object.ts
│ │ │ ├── quest.ts
│ │ │ ├── r2.ts
│ │ │ └── user.ts
│ │ ├── tsconfig.json
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── frontend
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── app
│ │ │ ├── chatbot
│ │ │ │ └── page.tsx
│ │ │ ├── favicon.ico
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ ├── manifest.ts
│ │ │ ├── page.tsx
│ │ │ ├── quests
│ │ │ │ ├── [questId]
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── complete
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── create
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── detail
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── layout.tsx
│ │ │ │ ├── page.tsx
│ │ │ │ └── position
│ │ │ │ └── page.tsx
│ │ │ └── start
│ │ │ └── page.tsx
│ │ ├── cloudflare-env.d.ts
│ │ ├── components
│ │ │ ├── auth
│ │ │ │ └── authInitializer.tsx
│ │ │ ├── layout
│ │ │ │ ├── bgSky.tsx
│ │ │ │ └── myTown.tsx
│ │ │ └── ui
│ │ │ ├── avatar.tsx
│ │ │ ├── badge.tsx
│ │ │ ├── button.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── input-group.tsx
│ │ │ ├── input.tsx
│ │ │ ├── label.tsx
│ │ │ ├── progress.tsx
│ │ │ ├── scroll-area.tsx
│ │ │ ├── select.tsx
│ │ │ ├── sonner.tsx
│ │ │ ├── spinner.tsx
│ │ │ └── textarea.tsx
│ │ ├── components.json
│ │ ├── features
│ │ │ ├── auth
│ │ │ │ ├── components
│ │ │ │ │ ├── authenticatedProfileDialog.tsx
│ │ │ │ │ ├── profileSection.tsx
│ │ │ │ │ ├── signInButton.tsx
│ │ │ │ │ ├── signInForm.tsx
│ │ │ │ │ └── userProfileCard.tsx
│ │ │ │ └── hooks
│ │ │ │ └── useSignIn.ts
│ │ │ ├── chatbot
│ │ │ │ └── components
│ │ │ │ └── chatBotPanel.tsx
│ │ │ ├── quest
│ │ │ │ └── components
│ │ │ │ ├── chat.tsx
│ │ │ │ ├── questCard.tsx
│ │ │ │ ├── questCreateForm.tsx
│ │ │ │ ├── saveObjectButton.tsx
│ │ │ │ └── submitButton.tsx
│ │ │ └── world3d
│ │ │ ├── components
│ │ │ │ ├── captureController.tsx
│ │ │ │ ├── ground.tsx
│ │ │ │ ├── hoverGuide.tsx
│ │ │ │ ├── resultObject.tsx
│ │ │ │ ├── rotationControl.tsx
│ │ │ │ ├── sceneSetup.tsx
│ │ │ │ └── selectPosition.tsx
│ │ │ ├── hooks
│ │ │ │ ├── useGetMaps.ts
│ │ │ │ └── useObjectPlacement.ts
│ │ │ └── utils
│ │ │ ├── buildingCalculations.ts
│ │ │ └── buildingRotation.ts
│ │ ├── hooks
│ │ │ └── useDeviceDetection.ts
│ │ ├── lib
│ │ │ ├── auth-client.ts
│ │ │ ├── rpc-client.ts
│ │ │ ├── text-shadow.ts
│ │ │ └── utils.ts
│ │ ├── next-env.d.ts
│ │ ├── next-sitemap.config.js
│ │ ├── next.config.ts
│ │ ├── open-next.config.ts
│ │ ├── package.json
│ │ ├── postcss.config.mjs
│ │ ├── public
│ │ │ ├── AICharacter.png
│ │ │ ├── house.png
│ │ │ ├── icon-192x192.png
│ │ │ ├── icon-512x512.png
│ │ │ ├── robots.txt
│ │ │ ├── sitemap-0.xml
│ │ │ └── sitemap.xml
│ │ ├── stores
│ │ │ ├── authStore.ts
│ │ │ ├── index.ts
│ │ │ └── objectStore.ts
│ │ ├── tsconfig.json
│ │ ├── types
│ │ │ └── index.ts
│ │ └── wrangler.jsonc
│ └── mcp
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── config.ts
│ │ ├── index.ts
│ │ └── tools
│ │ ├── githubTools.ts
│ │ └── vectorSearchTool.ts
│ ├── tsconfig.json
│ ├── worker-configuration.d.ts
│ └── wrangler.jsonc
├── biome.json
├── compose.yml
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── tree.txt