@einja/dev-cli 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/README.md +179 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +49 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/init.d.ts +3 -0
  7. package/dist/commands/init.d.ts.map +1 -0
  8. package/dist/commands/init.js +243 -0
  9. package/dist/commands/init.js.map +1 -0
  10. package/dist/commands/list.d.ts +2 -0
  11. package/dist/commands/list.d.ts.map +1 -0
  12. package/dist/commands/list.js +23 -0
  13. package/dist/commands/list.js.map +1 -0
  14. package/dist/commands/sync.d.ts +7 -0
  15. package/dist/commands/sync.d.ts.map +1 -0
  16. package/dist/commands/sync.js +294 -0
  17. package/dist/commands/sync.js.map +1 -0
  18. package/dist/commands/sync.test.d.ts +2 -0
  19. package/dist/commands/sync.test.d.ts.map +1 -0
  20. package/dist/commands/sync.test.js +593 -0
  21. package/dist/commands/sync.test.js.map +1 -0
  22. package/dist/commands/task-loop.d.ts +11 -0
  23. package/dist/commands/task-loop.d.ts.map +1 -0
  24. package/dist/commands/task-loop.js +81 -0
  25. package/dist/commands/task-loop.js.map +1 -0
  26. package/dist/index.d.ts +4 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +3 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/lib/file-system.d.ts +39 -0
  31. package/dist/lib/file-system.d.ts.map +1 -0
  32. package/dist/lib/file-system.js +79 -0
  33. package/dist/lib/file-system.js.map +1 -0
  34. package/dist/lib/mcp-config.d.ts +43 -0
  35. package/dist/lib/mcp-config.d.ts.map +1 -0
  36. package/dist/lib/mcp-config.js +109 -0
  37. package/dist/lib/mcp-config.js.map +1 -0
  38. package/dist/lib/mcp-config.test.d.ts +2 -0
  39. package/dist/lib/mcp-config.test.d.ts.map +1 -0
  40. package/dist/lib/mcp-config.test.js +285 -0
  41. package/dist/lib/mcp-config.test.js.map +1 -0
  42. package/dist/lib/merger.d.ts +41 -0
  43. package/dist/lib/merger.d.ts.map +1 -0
  44. package/dist/lib/merger.js +164 -0
  45. package/dist/lib/merger.js.map +1 -0
  46. package/dist/lib/preset-update/cli-repo-detector.d.ts +35 -0
  47. package/dist/lib/preset-update/cli-repo-detector.d.ts.map +1 -0
  48. package/dist/lib/preset-update/cli-repo-detector.js +83 -0
  49. package/dist/lib/preset-update/cli-repo-detector.js.map +1 -0
  50. package/dist/lib/preset-update/cli-repo-detector.test.d.ts +2 -0
  51. package/dist/lib/preset-update/cli-repo-detector.test.d.ts.map +1 -0
  52. package/dist/lib/preset-update/cli-repo-detector.test.js +120 -0
  53. package/dist/lib/preset-update/cli-repo-detector.test.js.map +1 -0
  54. package/dist/lib/preset-update/file-copier.d.ts +59 -0
  55. package/dist/lib/preset-update/file-copier.d.ts.map +1 -0
  56. package/dist/lib/preset-update/file-copier.js +220 -0
  57. package/dist/lib/preset-update/file-copier.js.map +1 -0
  58. package/dist/lib/preset-update/file-copier.test.d.ts +2 -0
  59. package/dist/lib/preset-update/file-copier.test.d.ts.map +1 -0
  60. package/dist/lib/preset-update/file-copier.test.js +297 -0
  61. package/dist/lib/preset-update/file-copier.test.js.map +1 -0
  62. package/dist/lib/preset-update/preset-finder.d.ts +39 -0
  63. package/dist/lib/preset-update/preset-finder.d.ts.map +1 -0
  64. package/dist/lib/preset-update/preset-finder.js +92 -0
  65. package/dist/lib/preset-update/preset-finder.js.map +1 -0
  66. package/dist/lib/preset-update/preset-finder.test.d.ts +2 -0
  67. package/dist/lib/preset-update/preset-finder.test.d.ts.map +1 -0
  68. package/dist/lib/preset-update/preset-finder.test.js +128 -0
  69. package/dist/lib/preset-update/preset-finder.test.js.map +1 -0
  70. package/dist/lib/preset.d.ts +14 -0
  71. package/dist/lib/preset.d.ts.map +1 -0
  72. package/dist/lib/preset.js +52 -0
  73. package/dist/lib/preset.js.map +1 -0
  74. package/dist/lib/sync/backup-manager.d.ts +50 -0
  75. package/dist/lib/sync/backup-manager.d.ts.map +1 -0
  76. package/dist/lib/sync/backup-manager.js +117 -0
  77. package/dist/lib/sync/backup-manager.js.map +1 -0
  78. package/dist/lib/sync/backup-manager.test.d.ts +2 -0
  79. package/dist/lib/sync/backup-manager.test.d.ts.map +1 -0
  80. package/dist/lib/sync/backup-manager.test.js +155 -0
  81. package/dist/lib/sync/backup-manager.test.js.map +1 -0
  82. package/dist/lib/sync/batch-processor.d.ts +27 -0
  83. package/dist/lib/sync/batch-processor.d.ts.map +1 -0
  84. package/dist/lib/sync/batch-processor.js +46 -0
  85. package/dist/lib/sync/batch-processor.js.map +1 -0
  86. package/dist/lib/sync/batch-processor.test.d.ts +2 -0
  87. package/dist/lib/sync/batch-processor.test.d.ts.map +1 -0
  88. package/dist/lib/sync/batch-processor.test.js +110 -0
  89. package/dist/lib/sync/batch-processor.test.js.map +1 -0
  90. package/dist/lib/sync/category-validator.d.ts +36 -0
  91. package/dist/lib/sync/category-validator.d.ts.map +1 -0
  92. package/dist/lib/sync/category-validator.js +46 -0
  93. package/dist/lib/sync/category-validator.js.map +1 -0
  94. package/dist/lib/sync/category-validator.test.d.ts +2 -0
  95. package/dist/lib/sync/category-validator.test.d.ts.map +1 -0
  96. package/dist/lib/sync/category-validator.test.js +89 -0
  97. package/dist/lib/sync/category-validator.test.js.map +1 -0
  98. package/dist/lib/sync/conflict-reporter.d.ts +57 -0
  99. package/dist/lib/sync/conflict-reporter.d.ts.map +1 -0
  100. package/dist/lib/sync/conflict-reporter.js +81 -0
  101. package/dist/lib/sync/conflict-reporter.js.map +1 -0
  102. package/dist/lib/sync/conflict-reporter.test.d.ts +2 -0
  103. package/dist/lib/sync/conflict-reporter.test.d.ts.map +1 -0
  104. package/dist/lib/sync/conflict-reporter.test.js +132 -0
  105. package/dist/lib/sync/conflict-reporter.test.js.map +1 -0
  106. package/dist/lib/sync/diff-engine.d.ts +28 -0
  107. package/dist/lib/sync/diff-engine.d.ts.map +1 -0
  108. package/dist/lib/sync/diff-engine.js +118 -0
  109. package/dist/lib/sync/diff-engine.js.map +1 -0
  110. package/dist/lib/sync/diff-engine.test.d.ts +2 -0
  111. package/dist/lib/sync/diff-engine.test.d.ts.map +1 -0
  112. package/dist/lib/sync/diff-engine.test.js +133 -0
  113. package/dist/lib/sync/diff-engine.test.js.map +1 -0
  114. package/dist/lib/sync/file-filter.d.ts +40 -0
  115. package/dist/lib/sync/file-filter.d.ts.map +1 -0
  116. package/dist/lib/sync/file-filter.js +171 -0
  117. package/dist/lib/sync/file-filter.js.map +1 -0
  118. package/dist/lib/sync/file-filter.test.d.ts +2 -0
  119. package/dist/lib/sync/file-filter.test.d.ts.map +1 -0
  120. package/dist/lib/sync/file-filter.test.js +179 -0
  121. package/dist/lib/sync/file-filter.test.js.map +1 -0
  122. package/dist/lib/sync/hash-cache.d.ts +34 -0
  123. package/dist/lib/sync/hash-cache.d.ts.map +1 -0
  124. package/dist/lib/sync/hash-cache.js +51 -0
  125. package/dist/lib/sync/hash-cache.js.map +1 -0
  126. package/dist/lib/sync/hash-cache.test.d.ts +2 -0
  127. package/dist/lib/sync/hash-cache.test.d.ts.map +1 -0
  128. package/dist/lib/sync/hash-cache.test.js +110 -0
  129. package/dist/lib/sync/hash-cache.test.js.map +1 -0
  130. package/dist/lib/sync/integration.test.d.ts +2 -0
  131. package/dist/lib/sync/integration.test.d.ts.map +1 -0
  132. package/dist/lib/sync/integration.test.js +317 -0
  133. package/dist/lib/sync/integration.test.js.map +1 -0
  134. package/dist/lib/sync/marker-processor.d.ts +54 -0
  135. package/dist/lib/sync/marker-processor.d.ts.map +1 -0
  136. package/dist/lib/sync/marker-processor.js +208 -0
  137. package/dist/lib/sync/marker-processor.js.map +1 -0
  138. package/dist/lib/sync/marker-processor.test.d.ts +2 -0
  139. package/dist/lib/sync/marker-processor.test.d.ts.map +1 -0
  140. package/dist/lib/sync/marker-processor.test.js +245 -0
  141. package/dist/lib/sync/marker-processor.test.js.map +1 -0
  142. package/dist/lib/sync/metadata-manager.d.ts +46 -0
  143. package/dist/lib/sync/metadata-manager.d.ts.map +1 -0
  144. package/dist/lib/sync/metadata-manager.js +129 -0
  145. package/dist/lib/sync/metadata-manager.js.map +1 -0
  146. package/dist/lib/sync/metadata-manager.test.d.ts +2 -0
  147. package/dist/lib/sync/metadata-manager.test.d.ts.map +1 -0
  148. package/dist/lib/sync/metadata-manager.test.js +137 -0
  149. package/dist/lib/sync/metadata-manager.test.js.map +1 -0
  150. package/dist/lib/sync/performance.test.d.ts +2 -0
  151. package/dist/lib/sync/performance.test.d.ts.map +1 -0
  152. package/dist/lib/sync/performance.test.js +126 -0
  153. package/dist/lib/sync/performance.test.js.map +1 -0
  154. package/dist/types/index.d.ts +59 -0
  155. package/dist/types/index.d.ts.map +1 -0
  156. package/dist/types/index.js +2 -0
  157. package/dist/types/index.js.map +1 -0
  158. package/dist/types/preset-update.d.ts +106 -0
  159. package/dist/types/preset-update.d.ts.map +1 -0
  160. package/dist/types/preset-update.js +5 -0
  161. package/dist/types/preset-update.js.map +1 -0
  162. package/dist/types/sync.d.ts +169 -0
  163. package/dist/types/sync.d.ts.map +1 -0
  164. package/dist/types/sync.js +19 -0
  165. package/dist/types/sync.js.map +1 -0
  166. package/package.json +72 -0
  167. package/presets/minimal/.claude/agents/einja/docs/docs-updater.md +161 -0
  168. package/presets/minimal/.claude/agents/einja/frontend/design-engineer.md +685 -0
  169. package/presets/minimal/.claude/agents/einja/frontend/frontend-architect.md +747 -0
  170. package/presets/minimal/.claude/agents/einja/frontend/frontend-coder.md +441 -0
  171. package/presets/minimal/.claude/agents/einja/git/conflict-resolver.md +148 -0
  172. package/presets/minimal/.claude/agents/einja/specs/spec-design-generator.md +462 -0
  173. package/presets/minimal/.claude/agents/einja/specs/spec-qa-generator.md +466 -0
  174. package/presets/minimal/.claude/agents/einja/specs/spec-requirements-generator.md +416 -0
  175. package/presets/minimal/.claude/agents/einja/specs/spec-tasks-generator.md +608 -0
  176. package/presets/minimal/.claude/agents/einja/task/task-committer.md +82 -0
  177. package/presets/minimal/.claude/agents/einja/task/task-executer.md +352 -0
  178. package/presets/minimal/.claude/agents/einja/task/task-modification-analyzer.md +369 -0
  179. package/presets/minimal/.claude/agents/einja/task/task-qa.md +74 -0
  180. package/presets/minimal/.claude/agents/einja/task/task-reviewer.md +169 -0
  181. package/presets/minimal/.claude/commands/einja/frontend-implement.md +322 -0
  182. package/presets/minimal/.claude/commands/einja/spec-create.md +254 -0
  183. package/presets/minimal/.claude/commands/einja/start-dev.md +98 -0
  184. package/presets/minimal/.claude/commands/einja/sync-cursor-commands.md +203 -0
  185. package/presets/minimal/.claude/commands/einja/task-exec.md +390 -0
  186. package/presets/minimal/.claude/commands/einja/update-docs-by-task-specs.md +448 -0
  187. package/presets/minimal/.claude/hooks/einja/biome-format.sh +49 -0
  188. package/presets/minimal/.claude/hooks/einja/design-doc-check.sh +61 -0
  189. package/presets/minimal/.claude/hooks/einja/detect-secrets.sh +62 -0
  190. package/presets/minimal/.claude/hooks/einja/large-file-warning.sh +42 -0
  191. package/presets/minimal/.claude/hooks/einja/playwright-resize.sh +36 -0
  192. package/presets/minimal/.claude/hooks/einja/typecheck.sh +37 -0
  193. package/presets/minimal/.claude/hooks/einja/unset-volta-recursion.sh +32 -0
  194. package/presets/minimal/.claude/hooks/einja/validate-git-commit.sh +239 -0
  195. package/presets/minimal/.claude/hooks/einja/warn-index-ts.sh +34 -0
  196. package/presets/minimal/.claude/hooks/einja/warn-relative-import.sh +48 -0
  197. package/presets/minimal/.claude/settings.json +174 -0
  198. package/presets/minimal/.claude/skills/einja/api-development/SKILL.md +14 -0
  199. package/presets/minimal/.claude/skills/einja/backend-architecture/SKILL.md +14 -0
  200. package/presets/minimal/.claude/skills/einja/coding-standards/SKILL.md +120 -0
  201. package/presets/minimal/.claude/skills/einja/coding-standards/reference/naming-conventions.md +107 -0
  202. package/presets/minimal/.claude/skills/einja/coding-standards/reference/prohibited-patterns.md +169 -0
  203. package/presets/minimal/.claude/skills/einja/coding-standards/reference/typescript-rules.md +247 -0
  204. package/presets/minimal/.claude/skills/einja/component-design/SKILL.md +109 -0
  205. package/presets/minimal/.claude/skills/einja/component-design/reference/directory-structure.md +117 -0
  206. package/presets/minimal/.claude/skills/einja/component-design/reference/props-patterns.md +159 -0
  207. package/presets/minimal/.claude/skills/einja/component-design/reference/styling-guide.md +200 -0
  208. package/presets/minimal/.claude/skills/einja/conflict-resolver/SKILL.md +190 -0
  209. package/presets/minimal/.claude/skills/einja/frontend-development/SKILL.md +14 -0
  210. package/presets/minimal/.claude/skills/einja/general-context-loader/SKILL.md +254 -0
  211. package/presets/minimal/.claude/skills/einja/output-format/SKILL.md +137 -0
  212. package/presets/minimal/.claude/skills/einja/spec-context-loader/SKILL.md +177 -0
  213. package/presets/minimal/.claude/skills/einja/task-commit/SKILL.md +269 -0
  214. package/presets/minimal/.claude/skills/einja/task-qa/SKILL.md +306 -0
  215. package/presets/minimal/.claude/skills/einja/task-qa/reference/failure-patterns.md +69 -0
  216. package/presets/minimal/.claude/skills/einja/task-qa/reference/troubleshooting.md +65 -0
  217. package/presets/minimal/.claude/skills/einja/task-qa/reference/usage-patterns.md +52 -0
  218. package/presets/minimal/.claude/skills/einja/task-qa/templates/qa-test-template.md +128 -0
  219. package/presets/minimal/preset.yaml +111 -0
  220. package/presets/minimal/symlinks.json +45 -0
  221. package/scaffolds/.mcp.json +45 -0
  222. package/scaffolds/CLAUDE.md.template +318 -0
  223. package/scaffolds/steering/README.md +170 -0
  224. package/scaffolds/steering/acceptance-criteria-and-qa-guide.md +415 -0
  225. package/scaffolds/steering/architecture.md +481 -0
  226. package/scaffolds/steering/branch-strategy.md +362 -0
  227. package/scaffolds/steering/commit-rules.md +217 -0
  228. package/scaffolds/steering/db-schema-design.md +609 -0
  229. package/scaffolds/steering/development/api-development.md +783 -0
  230. package/scaffolds/steering/development/backend-architecture.md +731 -0
  231. package/scaffolds/steering/development/frontend-development.md +1537 -0
  232. package/scaffolds/steering/development/review-guidelines.md +365 -0
  233. package/scaffolds/steering/development/testing-strategy.md +819 -0
  234. package/scaffolds/steering/development-workflow.md +429 -0
  235. package/scaffolds/steering/infrastructure/deployment.md +277 -0
  236. package/scaffolds/steering/infrastructure/environment-variables.md +298 -0
  237. package/scaffolds/steering/product.md +540 -0
  238. package/scaffolds/steering/task-management.md +367 -0
  239. package/templates/README.md +159 -0
  240. package/templates/design-simple.md.template +172 -0
  241. package/templates/design.md.template +327 -0
  242. package/templates/qa-test.md.template +125 -0
  243. package/templates/requirements.md.template +254 -0
@@ -0,0 +1,783 @@
1
+ # API開発ガイド
2
+
3
+ ## 概要
4
+
5
+ このドキュメントでは、Honoを使用したAPI開発の実装ガイドラインと、フロントエンドとの統合方法を説明します。
6
+
7
+ モノレポ全体で統一されたAPI設計パターンを採用し、型安全性とメンテナンス性を確保します。
8
+
9
+ ---
10
+
11
+ ## 目次
12
+
13
+ 1. [Hono API実装ルール](#1-hono-api実装ルール)
14
+ - [メソッドチェーンパターン](#メソッドチェーンパターン)
15
+ - [ミドルウェアと型推論の注意点](#ミドルウェアと型推論の注意点) ⚠️
16
+ - [basePathとHono Clientの関係](#basepathとhono-clientの関係) ⚠️
17
+ 2. [Web APIエンドポイント一覧](#2-web-apiエンドポイント一覧)
18
+ 3. [Admin APIエンドポイント一覧](#3-admin-apiエンドポイント一覧)
19
+ 4. [Cron Worker CLIコマンド](#4-cron-worker-cliコマンド)
20
+ 5. [バリデーション戦略](#5-バリデーション戦略)
21
+ 6. [エラーハンドリング](#6-エラーハンドリング)
22
+ 7. [フロントエンド統合パターン](#7-フロントエンド統合パターン)
23
+ 8. [認証とミドルウェア](#8-認証とミドルウェア)
24
+ 9. [レスポンス設計](#9-レスポンス設計)
25
+ 10. [環境変数管理](#10-環境変数管理)
26
+ 11. [実装例](#11-実装例)
27
+
28
+ ---
29
+
30
+ ## 1. Hono API実装ルール
31
+
32
+ ### Honoアプリケーション構造
33
+
34
+ Honoは型安全なWebフレームワークで、すべてのNext.js APIルートで使用します。
35
+
36
+ **エントリーポイント**:
37
+ ```
38
+ apps/web/app/api/[[...route]]/route.ts # Web API
39
+ apps/admin/app/api/[[...route]]/route.ts # Admin API
40
+ ```
41
+
42
+ **ルート定義の配置**:
43
+ ```
44
+ apps/web/server/routes/ # Webアプリ用ルート定義
45
+ apps/admin/server/routes/ # 管理画面用ルート定義
46
+ ```
47
+
48
+ ### メソッドチェーンパターン
49
+
50
+ Honoでは、**必ずメソッドチェーン形式**でルートを定義します。
51
+
52
+ **重要: メソッドチェーンを使用する理由**
53
+
54
+ Hono Clientの型推論は `typeof app` から型情報を抽出します。メソッドチェーンを使用しない場合、TypeScriptが各ルート定義の返り値型を追跡できず、`AppType`に完全なルート情報が含まれません。
55
+
56
+ ```typescript
57
+ // ❌ NG: 個別呼び出し - 型推論が損なわれる
58
+ const app = new Hono()
59
+ app.get('/posts', handler1) // 返り値が破棄される
60
+ app.post('/posts', handler2) // 返り値が破棄される
61
+ export type AppType = typeof app // ルート情報が不完全
62
+
63
+ // ✅ OK: メソッドチェーン - 完全な型推論
64
+ const app = new Hono()
65
+ .get('/posts', handler1)
66
+ .post('/posts', handler2)
67
+ export type AppType = typeof app // 全ルート情報を含む
68
+ ```
69
+
70
+ メソッドチェーンにより、Hono Client (`hc<AppType>`) でエンドツーエンドの型安全なAPI呼び出しが実現できます。
71
+
72
+ ### ミドルウェアと型推論の注意点
73
+
74
+ **サブルート内で`.use()`を使うと型推論が壊れる。メインアプリ側で適用すること。**
75
+
76
+ ```typescript
77
+ // ❌ NG: サブルート内で.use() → 型が ClientRequest<{}> になる
78
+ export const adminUserRoutes = new Hono()
79
+ .use("*", adminAuthMiddleware)
80
+ .delete("/:id", handler)
81
+
82
+ // ✅ OK: メインアプリ側で.use()を適用
83
+ const app = new Hono()
84
+ .basePath("/api")
85
+ .use("/admin/*", adminAuthMiddleware) // ← ここで適用
86
+ .route("/admin", adminApp)
87
+ ```
88
+
89
+ ### basePathとHono Clientの関係
90
+
91
+ `basePath("/api")` を使用する場合、クライアント側も `api` を含めてアクセスする。
92
+
93
+ ```typescript
94
+ // サーバー: basePath("/api") を設定
95
+ const app = new Hono().basePath("/api").route("/posts", postRoutes)
96
+
97
+ // クライアント: api を含める
98
+ apiClient.api.posts.$get() // ✅ OK
99
+ apiClient.posts.$get() // ❌ NG(型エラー)
100
+ ```
101
+
102
+ **使用例**:
103
+
104
+ ```typescript
105
+ // apps/web/server/routes/postRoutes.ts
106
+ import { Hono } from 'hono'
107
+ import { zValidator } from '@hono/zod-validator'
108
+ import { createPostSchema } from '@repo/server-core/domain/validators/post'
109
+
110
+ const app = new Hono()
111
+
112
+ app
113
+ .get('/posts', async (c) => {
114
+ // GET /api/posts - 投稿一覧取得
115
+ const page = Number(c.req.query('page') || '1')
116
+ const limit = Number(c.req.query('limit') || '10')
117
+ // UseCase呼び出し...
118
+ return c.json({ posts, total })
119
+ })
120
+ .post('/posts', zValidator('json', createPostSchema), async (c) => {
121
+ // POST /api/posts - 投稿作成
122
+ const data = c.req.valid('json')
123
+ // UseCase呼び出し...
124
+ return c.json({ post }, 201)
125
+ })
126
+
127
+ export default app
128
+ ```
129
+
130
+ ### zValidatorの統合
131
+
132
+ **必須**: すべてのリクエストボディとクエリパラメータは、zValidatorでバリデーションを行う。
133
+
134
+ ```typescript
135
+ import { zValidator } from '@hono/zod-validator'
136
+ import { createPostSchema } from '@repo/server-core/domain/validators/post'
137
+
138
+ app.post('/posts', zValidator('json', createPostSchema), async (c) => {
139
+ const data = c.req.valid('json') // 型安全にバリデート済みデータを取得
140
+ // data は CreatePostInput 型として推論される
141
+ })
142
+ ```
143
+
144
+ ### ルートハンドラの構造
145
+
146
+ すべてのルートハンドラは、以下のパターンに従います:
147
+
148
+ ```typescript
149
+ async (c) => {
150
+ // 1. リクエストパラメータ取得(クエリ、パス、ボディ)
151
+ const data = c.req.valid('json')
152
+ const id = c.req.param('id')
153
+
154
+ // 2. UseCaseの呼び出し
155
+ const result = await postUseCases.create(data)
156
+
157
+ // 3. Result型の処理
158
+ if (!result.isSuccess) {
159
+ // エラーハンドリング(後述)
160
+ return c.json({ error: result.error.message }, 400)
161
+ }
162
+
163
+ // 4. レスポンス返却
164
+ return c.json({ post: result.value }, 201)
165
+ }
166
+ ```
167
+
168
+ ---
169
+
170
+ ## 2. Web APIエンドポイント一覧
171
+
172
+ ### ヘルスチェック
173
+
174
+ | メソッド | エンドポイント | 説明 | リクエスト | レスポンス | 認証 |
175
+ |---------|---------------|------|-----------|-----------|------|
176
+ | GET | `/api/health` | システム稼働確認 | - | `{status: "ok"}` | 不要 |
177
+
178
+ ### 認証エンドポイント
179
+
180
+ | メソッド | エンドポイント | 説明 | リクエスト | レスポンス | 認証 |
181
+ |---------|---------------|------|-----------|-----------|------|
182
+ | POST | `/api/auth/login` | ユーザーログイン | `{email, password}` | `{token, user}` | 不要 |
183
+ | POST | `/api/auth/logout` | ログアウト | - | `{success: true}` | 必要 |
184
+ | GET | `/api/auth/session` | セッション確認 | - | `{user}` | 必要 |
185
+
186
+ ### 投稿エンドポイント
187
+
188
+ | メソッド | エンドポイント | 説明 | リクエスト | レスポンス | 認証 |
189
+ |---------|---------------|------|-----------|-----------|------|
190
+ | GET | `/api/posts` | 投稿一覧取得 | `?page=1&limit=10` | `{posts[], total}` | オプション |
191
+ | POST | `/api/posts` | 投稿作成 | `{title, content, status?}` | `{post}` | 必要 |
192
+ | GET | `/api/posts/:id` | 投稿詳細取得 | - | `{post}` | オプション |
193
+ | PUT | `/api/posts/:id` | 投稿更新 | `{title?, content?, status?}` | `{post}` | 必要 |
194
+ | DELETE | `/api/posts/:id` | 投稿削除 | - | `{success: true}` | 必要 |
195
+
196
+ **ページネーション設計**:
197
+ - `page`: ページ番号(デフォルト: 1)
198
+ - `limit`: 1ページあたりの件数(デフォルト: 10、最大: 100)
199
+ - レスポンス: `{ posts: Post[], total: number }` - totalは全件数
200
+
201
+ **認証制御**:
202
+ - 投稿一覧・詳細: 認証不要(公開投稿のみ取得)
203
+ - 投稿作成・更新・削除: 認証必須
204
+ - 更新・削除: 投稿の所有者チェック(`post.isOwnedBy(userId)`)
205
+
206
+ ---
207
+
208
+ ## 3. Admin APIエンドポイント一覧
209
+
210
+ ### ヘルスチェック
211
+
212
+ | メソッド | エンドポイント | 説明 | リクエスト | レスポンス | 認証 |
213
+ |---------|---------------|------|-----------|-----------|------|
214
+ | GET | `/api/health` | システム稼働確認 | - | `{status: "ok"}` | 不要 |
215
+
216
+ ### ユーザー管理
217
+
218
+ | メソッド | エンドポイント | 説明 | リクエスト | レスポンス | 認証 |
219
+ |---------|---------------|------|-----------|-----------|------|
220
+ | GET | `/api/admin/users` | ユーザー一覧取得 | `?page=1&limit=20` | `{users[], total}` | 管理者 |
221
+ | GET | `/api/admin/users/:id` | ユーザー詳細取得 | - | `{user}` | 管理者 |
222
+ | PUT | `/api/admin/users/:id` | ユーザー情報更新 | `{name?, email?}` | `{user}` | 管理者 |
223
+ | DELETE | `/api/admin/users/:id` | ユーザー削除 | - | `{success: true}` | 管理者 |
224
+
225
+ ### 投稿管理
226
+
227
+ | メソッド | エンドポイント | 説明 | リクエスト | レスポンス | 認証 |
228
+ |---------|---------------|------|-----------|-----------|------|
229
+ | GET | `/api/admin/posts` | 全投稿一覧取得 | `?status=all&page=1` | `{posts[], total}` | 管理者 |
230
+ | PUT | `/api/admin/posts/:id/status` | 投稿ステータス変更 | `{status}` | `{post}` | 管理者 |
231
+
232
+ ### 分析
233
+
234
+ | メソッド | エンドポイント | 説明 | リクエスト | レスポンス | 認証 |
235
+ |---------|---------------|------|-----------|-----------|------|
236
+ | GET | `/api/admin/analytics` | システム統計取得 | `?from=&to=` | `{stats}` | 管理者 |
237
+
238
+ **管理者認証**:
239
+ - すべてのAdmin APIは、管理者権限(`role='admin'`)のチェックが必要
240
+ - ミドルウェアで JWT検証 + role チェックを実施
241
+
242
+ ---
243
+
244
+ ## 4. Cron Worker CLIコマンド
245
+
246
+ cron-workerは、HTTP APIではなく**CLIコマンド**として実装されます。
247
+
248
+ Railway CronまたはVercel Cronなどの外部スケジューラーから直接実行されます。
249
+
250
+ ### コマンド一覧
251
+
252
+ | コマンド | 説明 | 実行方法 | 実装ファイル |
253
+ |---------|------|---------|------------|
254
+ | `pnpm job:cleanup` | 期限切れセッションのクリーンアップ | `tsx src/jobs/cleanup.ts` | `apps/cron-worker/src/jobs/cleanup.ts` |
255
+ | `pnpm job:email-digest` | メールダイジェスト送信 | `tsx src/jobs/email-digest.ts` | `apps/cron-worker/src/jobs/email-digest.ts` |
256
+ | `pnpm job:health-check` | ジョブシステムの稼働確認 | `tsx src/jobs/health-check.ts` | `apps/cron-worker/src/jobs/health-check.ts` |
257
+
258
+ ### 実装方針
259
+
260
+ - **tsxによるTypeScript直接実行**: ビルド不要で実行可能
261
+ - **Repositoryパターン使用**: `@repo/server-core`の Repository と UseCase を活用
262
+ - **JobCoordinator機構**: 重複実行防止とエラーハンドリング
263
+ - **ログ出力**: すべてのジョブで実行結果をログ出力
264
+ - **終了コード**: 成功時は0、失敗時は非0を返却
265
+
266
+ ### package.json設定例
267
+
268
+ ```json
269
+ {
270
+ "scripts": {
271
+ "job:cleanup": "dotenv -e ../../.env -e .env.local -- tsx src/jobs/cleanup.ts",
272
+ "job:email-digest": "dotenv -e ../../.env -e .env.local -- tsx src/jobs/email-digest.ts",
273
+ "job:health-check": "dotenv -e ../../.env -e .env.local -- tsx src/jobs/health-check.ts"
274
+ }
275
+ }
276
+ ```
277
+
278
+ ---
279
+
280
+ ## 5. バリデーション戦略
281
+
282
+ ### Zodスキーマ定義
283
+
284
+ すべてのリクエストボディとレスポンスは、Zodスキーマで定義します。
285
+
286
+ **配置場所**: `packages/server-core/src/domain/validators/`
287
+
288
+ **スキーマ例**:
289
+
290
+ ```typescript
291
+ // packages/server-core/src/domain/validators/post.ts
292
+ import { z } from 'zod'
293
+
294
+ export const createPostSchema = z.object({
295
+ title: z.string().min(1).max(200),
296
+ content: z.string().min(1),
297
+ status: z.enum(['draft', 'published']).default('draft'),
298
+ })
299
+
300
+ export const updatePostSchema = z.object({
301
+ title: z.string().min(1).max(200).optional(),
302
+ content: z.string().min(1).optional(),
303
+ status: z.enum(['draft', 'published', 'archived']).optional(),
304
+ })
305
+
306
+ export type CreatePostInput = z.infer<typeof createPostSchema>
307
+ export type UpdatePostInput = z.infer<typeof updatePostSchema>
308
+ ```
309
+
310
+ ### zValidatorの使用
311
+
312
+ **リクエストボディのバリデーション**:
313
+
314
+ ```typescript
315
+ import { zValidator } from '@hono/zod-validator'
316
+ import { createPostSchema } from '@repo/server-core/domain/validators/post'
317
+
318
+ app.post('/posts', zValidator('json', createPostSchema), async (c) => {
319
+ const data = c.req.valid('json') // バリデート済みデータを型安全に取得
320
+ // data は CreatePostInput 型として推論される
321
+ })
322
+ ```
323
+
324
+ **クエリパラメータのバリデーション**:
325
+
326
+ ```typescript
327
+ const querySchema = z.object({
328
+ page: z.string().transform(Number).default('1'),
329
+ limit: z.string().transform(Number).default('10'),
330
+ })
331
+
332
+ app.get('/posts', zValidator('query', querySchema), async (c) => {
333
+ const { page, limit } = c.req.valid('query')
334
+ })
335
+ ```
336
+
337
+ ### 型の自動生成
338
+
339
+ Zodスキーマから型を自動生成することで、型の重複を防ぎます:
340
+
341
+ ```typescript
342
+ export type CreatePostInput = z.infer<typeof createPostSchema>
343
+ ```
344
+
345
+ この型は、フロントエンドとバックエンドで共有されます。
346
+
347
+ ---
348
+
349
+ ## 6. エラーハンドリング
350
+
351
+ ### ApplicationError階層
352
+
353
+ `@repo/server-core/utils/errors.ts`で定義されたエラークラス階層を使用します。
354
+
355
+ ```typescript
356
+ // ApplicationError 基底クラス
357
+ class ApplicationError extends Error {
358
+ constructor(
359
+ public code: string,
360
+ message: string,
361
+ public statusCode: number = 500
362
+ ) {
363
+ super(message)
364
+ }
365
+ }
366
+
367
+ // 派生エラークラス
368
+ class ValidationError extends ApplicationError {
369
+ constructor(message: string) {
370
+ super('VALIDATION_ERROR', message, 400)
371
+ }
372
+ }
373
+
374
+ class NotFoundError extends ApplicationError {
375
+ constructor(message: string) {
376
+ super('NOT_FOUND', message, 404)
377
+ }
378
+ }
379
+
380
+ class UnauthorizedError extends ApplicationError {
381
+ constructor(message: string) {
382
+ super('UNAUTHORIZED', message, 401)
383
+ }
384
+ }
385
+
386
+ class ForbiddenError extends ApplicationError {
387
+ constructor(message: string) {
388
+ super('FORBIDDEN', message, 403)
389
+ }
390
+ }
391
+
392
+ class DatabaseError extends ApplicationError {
393
+ constructor(message: string) {
394
+ super('DATABASE_ERROR', message, 500)
395
+ }
396
+ }
397
+ ```
398
+
399
+ ### Result型からApiResponseへの変換
400
+
401
+ UseCaseは`Result<T, E>`型を返却しますが、APIレスポンスは`ApiResponse`形式に変換します。
402
+
403
+ ```typescript
404
+ // ApiResponse型定義
405
+ type ApiResponse<T> = {
406
+ data?: T
407
+ error?: {
408
+ code: string
409
+ message: string
410
+ }
411
+ }
412
+
413
+ // Result → ApiResponse 変換パターン
414
+ app.post('/posts', zValidator('json', createPostSchema), async (c) => {
415
+ const data = c.req.valid('json')
416
+ const result = await postUseCases.create(data)
417
+
418
+ if (!result.isSuccess) {
419
+ const error = result.error
420
+ return c.json(
421
+ { error: { code: error.code, message: error.message } },
422
+ error.statusCode
423
+ )
424
+ }
425
+
426
+ return c.json({ data: result.value }, 201)
427
+ })
428
+ ```
429
+
430
+ ### HTTPステータスコードマッピング
431
+
432
+ | エラー種別 | HTTPステータス | 説明 |
433
+ |----------|--------------|------|
434
+ | ValidationError | 400 | リクエストデータが不正 |
435
+ | UnauthorizedError | 401 | 認証が必要 |
436
+ | ForbiddenError | 403 | 権限不足 |
437
+ | NotFoundError | 404 | リソースが存在しない |
438
+ | DatabaseError | 500 | データベースエラー |
439
+ | ApplicationError | 500 | その他のサーバーエラー |
440
+
441
+ ---
442
+
443
+ ## 7. フロントエンド統合パターン
444
+
445
+ フロントエンドからAPIを呼び出す方法には、**Server Actions**と**Hono Client + Tanstack Query**の2つのパターンがあります。
446
+
447
+ ### パターン比較
448
+
449
+ | 観点 | Server Actions | Hono Client + Tanstack Query |
450
+ |------|---------------|------------------------------|
451
+ | **複雑さ** | シンプル | やや複雑 |
452
+ | **キャッシュ** | Next.jsのrevalidate | Tanstack Queryの高度なキャッシュ |
453
+ | **楽観的更新** | 手動実装 | 組み込みサポート |
454
+ | **ローディング状態** | useFormStatus/useTransition | isPending/isLoading |
455
+ | **エラーハンドリング** | try-catch | onError コールバック |
456
+ | **リアルタイム更新** | 不向き | refetchInterval等で対応可 |
457
+ | **型安全性** | 高い | 高い(RPC型推論) |
458
+
459
+ ### 使い分け基準
460
+
461
+ #### ✅ Server Actionsを使う場合
462
+
463
+ - **シンプルなフォーム送信**(お問い合わせ、ログイン等)
464
+ - **単発のミューテーション**(いいね、フォロー等)
465
+ - **SEOが重要なページ**でのデータ更新
466
+ - **Progressive Enhancement**が必要な場合(JS無効でも動作)
467
+ - **キャッシュ管理が単純**な場合
468
+
469
+ ```typescript
470
+ // app/actions/post.ts
471
+ 'use server'
472
+
473
+ import { revalidatePath } from 'next/cache'
474
+ import { postUseCases } from '@/application/use-cases/PostUseCases'
475
+ import { createPostSchema } from '@repo/server-core/domain/validators/post'
476
+
477
+ export async function createPost(formData: FormData) {
478
+ const rawData = {
479
+ title: formData.get('title'),
480
+ content: formData.get('content'),
481
+ status: formData.get('status') || 'draft',
482
+ }
483
+
484
+ // バリデーション
485
+ const parsed = createPostSchema.safeParse(rawData)
486
+ if (!parsed.success) {
487
+ return { error: parsed.error.flatten().fieldErrors }
488
+ }
489
+
490
+ // UseCase呼び出し
491
+ const result = await postUseCases.create(parsed.data)
492
+
493
+ if (!result.isSuccess) {
494
+ return { error: { _form: [result.error.message] } }
495
+ }
496
+
497
+ // キャッシュ無効化
498
+ revalidatePath('/posts')
499
+
500
+ return { success: true, post: result.value }
501
+ }
502
+ ```
503
+
504
+ ```typescript
505
+ // components/features/posts/PostCreateForm.tsx
506
+ 'use client'
507
+
508
+ import { useActionState } from 'react'
509
+ import { createPost } from '@/app/actions/post'
510
+
511
+ export function PostCreateForm() {
512
+ const [state, formAction, isPending] = useActionState(createPost, null)
513
+
514
+ return (
515
+ <form action={formAction}>
516
+ <input name="title" required />
517
+ {state?.error?.title && <p className="error">{state.error.title}</p>}
518
+
519
+ <textarea name="content" required />
520
+ {state?.error?.content && <p className="error">{state.error.content}</p>}
521
+
522
+ <button type="submit" disabled={isPending}>
523
+ {isPending ? '作成中...' : '投稿を作成'}
524
+ </button>
525
+
526
+ {state?.error?._form && <p className="error">{state.error._form}</p>}
527
+ </form>
528
+ )
529
+ }
530
+ ```
531
+
532
+ #### ✅ Hono Client + Tanstack Queryを使う場合
533
+
534
+ - **複数コンポーネントでデータ共有**が必要
535
+ - **高度なキャッシュ管理**(staleTime、cacheTime、条件付きrefetch)
536
+ - **楽観的更新**が必要(即座にUIに反映)
537
+ - **ポーリング・リアルタイム更新**が必要
538
+ - **複雑なデータフェッチ**(依存クエリ、並列クエリ)
539
+ - **ページネーション・無限スクロール**
540
+
541
+ 詳細な実装方法は **[フロントエンド開発ガイド](frontend-development.md)** を参照してください:
542
+ - セクション3: Hono Client統合
543
+ - セクション5: Tanstack Query
544
+ - セクション6: React Hook Form
545
+
546
+ ### 推奨パターン早見表
547
+
548
+ | ユースケース | 推奨パターン |
549
+ |------------|-------------|
550
+ | ログインフォーム | Server Actions |
551
+ | お問い合わせフォーム | Server Actions |
552
+ | いいねボタン | Server Actions |
553
+ | 投稿一覧(ページネーション付き) | Hono Client + Tanstack Query |
554
+ | コメント機能(リアルタイム更新) | Hono Client + Tanstack Query |
555
+ | 管理画面のCRUD | Hono Client + Tanstack Query |
556
+ | 検索機能(デバウンス付き) | Hono Client + Tanstack Query |
557
+ | ファイルアップロード | Server Actions |
558
+
559
+ ---
560
+
561
+ ## 8. 認証とミドルウェア
562
+
563
+ ### 認証ミドルウェア
564
+
565
+ ```typescript
566
+ // apps/web/server/middleware/auth.ts
567
+ import { createMiddleware } from 'hono/factory'
568
+ import { verify } from 'jsonwebtoken'
569
+
570
+ export const authMiddleware = createMiddleware(async (c, next) => {
571
+ const token = c.req.header('Authorization')?.replace('Bearer ', '')
572
+
573
+ if (!token) {
574
+ return c.json({ error: 'Unauthorized' }, 401)
575
+ }
576
+
577
+ try {
578
+ const payload = verify(token, process.env.JWT_SECRET!)
579
+ c.set('user', payload) // コンテキストにユーザー情報を設定
580
+ await next()
581
+ } catch (error) {
582
+ return c.json({ error: 'Invalid token' }, 401)
583
+ }
584
+ })
585
+ ```
586
+
587
+ ### 管理者権限チェックミドルウェア
588
+
589
+ ```typescript
590
+ // apps/admin/server/middleware/admin.ts
591
+ import { createMiddleware } from 'hono/factory'
592
+ import { authMiddleware } from './auth'
593
+
594
+ export const adminMiddleware = createMiddleware(async (c, next) => {
595
+ await authMiddleware(c, async () => {
596
+ const user = c.get('user')
597
+
598
+ if (user.role !== 'admin') {
599
+ return c.json({ error: 'Forbidden' }, 403)
600
+ }
601
+
602
+ await next()
603
+ })
604
+ })
605
+ ```
606
+
607
+ ### ミドルウェアの適用
608
+
609
+ ```typescript
610
+ import { Hono } from 'hono'
611
+ import { authMiddleware } from '@/server/middleware/auth'
612
+
613
+ const app = new Hono()
614
+
615
+ // 認証不要なルート
616
+ app.get('/health', (c) => c.json({ status: 'ok' }))
617
+
618
+ // 認証が必要なルート
619
+ app.use('/posts/*', authMiddleware)
620
+ app.post('/posts', async (c) => {
621
+ const user = c.get('user') // ミドルウェアで設定されたユーザー情報
622
+ // ...
623
+ })
624
+ ```
625
+
626
+ **⚠️ 重要**: サブルート内で`.use()`を使用するとHono RPC型推論が壊れます。
627
+ 詳細は[ミドルウェアと型推論の注意点](#ミドルウェアと型推論の注意点)を参照してください。
628
+
629
+ ---
630
+
631
+ ## 9. レスポンス設計
632
+
633
+ ### 統一レスポンス形式
634
+
635
+ すべてのAPIレスポンスは、以下の形式に従います:
636
+
637
+ **成功時**:
638
+ ```typescript
639
+ {
640
+ data: T // 実際のデータ
641
+ }
642
+ ```
643
+
644
+ **エラー時**:
645
+ ```typescript
646
+ {
647
+ error: {
648
+ code: string // エラーコード(例: "VALIDATION_ERROR")
649
+ message: string // エラーメッセージ
650
+ }
651
+ }
652
+ ```
653
+
654
+ ### ページネーションレスポンス
655
+
656
+ ```typescript
657
+ {
658
+ data: {
659
+ items: T[] // データ配列
660
+ total: number // 全件数
661
+ }
662
+ }
663
+ ```
664
+
665
+ ---
666
+
667
+ ## 10. 環境変数管理
668
+
669
+ ### dotenv-cliの使用
670
+
671
+ 環境変数は、dotenv-cliを使用して階層的にロードされます。
672
+
673
+ **ロード順序**(後勝ち):
674
+ 1. `root/.env` - モノレポ全体の共通設定
675
+ 2. `root/.env.local` - ローカル環境でのオーバーライド
676
+ 3. `apps/*/​.env.local` - アプリケーション固有のオーバーライド
677
+
678
+ **package.jsonでの設定**:
679
+
680
+ ```json
681
+ {
682
+ "scripts": {
683
+ "dev": "dotenv -e ../../.env -e ../../.env.local -e .env.local -- next dev",
684
+ "build": "dotenv -e ../../.env -e .env.local -- next build"
685
+ }
686
+ }
687
+ ```
688
+
689
+ ### Worktree対応の動的ポート割り当て
690
+
691
+ Worktreeごとに異なるポートを使用するため、MD5ハッシュベースの動的ポート割り当てを実装します。
692
+
693
+ 詳細は`deployment.md`を参照してください。
694
+
695
+ ---
696
+
697
+ ## 11. 実装例
698
+
699
+ ### 完全なPOST /api/posts実装
700
+
701
+ **ルート定義**:
702
+
703
+ ```typescript
704
+ // apps/web/server/routes/postRoutes.ts
705
+ import { Hono } from 'hono'
706
+ import { zValidator } from '@hono/zod-validator'
707
+ import { createPostSchema } from '@repo/server-core/domain/validators/post'
708
+ import { postUseCases } from '@repo/server-core/application/use-cases/postUseCases'
709
+ import { authMiddleware } from '@/server/middleware/auth'
710
+
711
+ const app = new Hono()
712
+
713
+ app.post(
714
+ '/posts',
715
+ authMiddleware, // 認証ミドルウェア
716
+ zValidator('json', createPostSchema), // バリデーション
717
+ async (c) => {
718
+ // 1. バリデート済みデータの取得
719
+ const data = c.req.valid('json')
720
+ const user = c.get('user') // 認証ユーザー情報
721
+
722
+ // 2. UseCaseの呼び出し
723
+ const result = await postUseCases.create({
724
+ ...data,
725
+ userId: user.id,
726
+ })
727
+
728
+ // 3. Result型の処理
729
+ if (!result.isSuccess) {
730
+ const error = result.error
731
+ return c.json(
732
+ { error: { code: error.code, message: error.message } },
733
+ error.statusCode
734
+ )
735
+ }
736
+
737
+ // 4. 成功レスポンス
738
+ return c.json({ data: result.value }, 201)
739
+ }
740
+ )
741
+
742
+ export default app
743
+ ```
744
+
745
+ **エントリーポイント**:
746
+
747
+ ```typescript
748
+ // apps/web/app/api/[[...route]]/route.ts
749
+ import { Hono } from 'hono'
750
+ import { handle } from 'hono/vercel'
751
+ import postRoutes from '@/server/routes/postRoutes'
752
+
753
+ const app = new Hono().basePath('/api')
754
+
755
+ app.route('/', postRoutes)
756
+
757
+ export const GET = handle(app)
758
+ export const POST = handle(app)
759
+ export const PUT = handle(app)
760
+ export const DELETE = handle(app)
761
+
762
+ export type AppType = typeof app
763
+ ```
764
+
765
+ **フロントエンドでの使用**:
766
+
767
+ フロントエンドからこのAPIを呼び出す実装例は **[フロントエンド開発ガイド](frontend-development.md)** を参照してください:
768
+
769
+ - **Server Actionsパターン**: セクション7「フロントエンド統合パターン」(本ドキュメント)
770
+ - **Hono Client + Tanstack Queryパターン**: [フロントエンド開発ガイド](frontend-development.md) セクション11
771
+
772
+ ---
773
+
774
+ ## まとめ
775
+
776
+ このAPI開発ガイドに従うことで、以下を実現できます:
777
+
778
+ 1. **型安全性**: Hono + Zod + TypeScriptによるエンドツーエンドの型推論
779
+ 2. **統一性**: モノレポ全体で統一されたAPI設計パターン
780
+ 3. **保守性**: Result型パターンとエラーハンドリングによる明確な制御フロー
781
+ 4. **柔軟性**: Server ActionsとHono Client + Tanstack Queryの使い分け
782
+
783
+ すべてのAPI実装は、このガイドラインに従って実装してください。