@einja/dev-cli 0.1.45 → 0.1.49
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.
- package/dist/cli.js +7 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +50 -11
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/sync.test.js +1 -1
- package/dist/commands/sync.test.js.map +1 -1
- package/dist/lib/sync/file-filter.d.ts.map +1 -1
- package/dist/lib/sync/file-filter.js +49 -0
- package/dist/lib/sync/file-filter.js.map +1 -1
- package/dist/lib/sync/file-filter.test.js +40 -0
- package/dist/lib/sync/file-filter.test.js.map +1 -1
- package/dist/lib/sync/json-processor.d.ts +49 -27
- package/dist/lib/sync/json-processor.d.ts.map +1 -1
- package/dist/lib/sync/json-processor.js +182 -82
- package/dist/lib/sync/json-processor.js.map +1 -1
- package/dist/lib/sync/json-processor.test.d.ts +2 -0
- package/dist/lib/sync/json-processor.test.d.ts.map +1 -0
- package/dist/lib/sync/json-processor.test.js +334 -0
- package/dist/lib/sync/json-processor.test.js.map +1 -0
- package/dist/lib/sync/metadata-manager.d.ts +6 -1
- package/dist/lib/sync/metadata-manager.d.ts.map +1 -1
- package/dist/lib/sync/metadata-manager.js +26 -6
- package/dist/lib/sync/metadata-manager.js.map +1 -1
- package/dist/types/sync.d.ts +2 -0
- package/dist/types/sync.d.ts.map +1 -1
- package/dist/types/sync.js +1 -0
- package/dist/types/sync.js.map +1 -1
- package/package.json +1 -1
- package/presets/default/.claude/agents/einja/backend-architect.md +27 -17
- package/presets/default/.claude/commands/einja/einja-sync.md +6 -6
- package/presets/default/.claude/skills/einja-coding-standards/references/testing-strategy.md +3 -3
- package/presets/default/docs/einja/instructions/setup-flow.md +40 -0
- package/presets/default/docs/einja/steering/architecture.md +7 -1
- package/presets/default/docs/einja/steering/development/api-development.md +199 -67
- package/presets/default/docs/einja/steering/development/backend-architecture.md +12 -16
- package/presets/default/docs/einja/steering/development/frontend-development.md +61 -50
- package/presets/default/docs/einja/steering/development/review-guidelines.md +4 -1
- package/presets/default/docs/einja/steering/development/testing-strategy.md +3 -3
- package/presets/default/package.json +73 -0
- package/presets/default/preset.yaml +2 -2
- package/presets/default/scripts/ensure-serena.sh +26 -7
|
@@ -104,10 +104,10 @@ AskUserQuestion:
|
|
|
104
104
|
|
|
105
105
|
```bash
|
|
106
106
|
# dev-cli の場合
|
|
107
|
-
npx --yes @einja/dev-cli sync --only <categories> --dry-run --yes
|
|
107
|
+
npx --yes @einja/dev-cli@latest sync --only <categories> --dry-run --yes
|
|
108
108
|
|
|
109
109
|
# create-einja-app の場合
|
|
110
|
-
npx --yes create-einja-app sync --categories <categories> --dry-run
|
|
110
|
+
npx --yes create-einja-app@latest sync --categories <categories> --dry-run
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
> **注**: 両CLIでカテゴリ指定のオプション名が異なる(dev-cli: `--only` / create-einja-app: `--categories`)
|
|
@@ -156,13 +156,13 @@ AskUserQuestion:
|
|
|
156
156
|
|
|
157
157
|
```bash
|
|
158
158
|
# dev-cli(孤児削除あり)
|
|
159
|
-
npx --yes @einja/dev-cli sync --only <categories> --yes --json --clean
|
|
159
|
+
npx --yes @einja/dev-cli@latest sync --only <categories> --yes --json --clean
|
|
160
160
|
|
|
161
161
|
# dev-cli(孤児削除なし)
|
|
162
|
-
npx --yes @einja/dev-cli sync --only <categories> --yes --json
|
|
162
|
+
npx --yes @einja/dev-cli@latest sync --only <categories> --yes --json
|
|
163
163
|
|
|
164
164
|
# create-einja-app
|
|
165
|
-
npx --yes create-einja-app sync --categories <categories>
|
|
165
|
+
npx --yes create-einja-app@latest sync --categories <categories>
|
|
166
166
|
```
|
|
167
167
|
|
|
168
168
|
- dev-cli は `--json` オプションで構造化出力を取得しパースする
|
|
@@ -228,7 +228,7 @@ f. 解消結果を `Read` で確認し、ユーザーに表示
|
|
|
228
228
|
|
|
229
229
|
Step 4 で孤児削除を選択した場合、JSON出力の `summary.orphansDeleted` を確認し、削除されたファイル一覧を記録する(Step 9 の詳細表示で使用)。
|
|
230
230
|
|
|
231
|
-
Step 4 で孤児削除をスキップした場合、孤児ファイル一覧を再表示し「後で `npx --yes @einja/dev-cli sync --only <categories> --clean --yes` で削除できます」と案内する。
|
|
231
|
+
Step 4 で孤児削除をスキップした場合、孤児ファイル一覧を再表示し「後で `npx --yes @einja/dev-cli@latest sync --only <categories> --clean --yes` で削除できます」と案内する。
|
|
232
232
|
|
|
233
233
|
### Step 7: direnv allow 実行確認
|
|
234
234
|
|
package/presets/default/.claude/skills/einja-coding-standards/references/testing-strategy.md
CHANGED
|
@@ -425,7 +425,7 @@ describe('UserRepository - データベースエラー', () => {
|
|
|
425
425
|
|
|
426
426
|
```typescript
|
|
427
427
|
// apps/web/__tests__/integration/post-api.test.ts
|
|
428
|
-
import {
|
|
428
|
+
import { rpc } from '@/lib/api/rpc'
|
|
429
429
|
import { prisma } from '@repo/server-core/infrastructure/database/client'
|
|
430
430
|
|
|
431
431
|
describe('Post API統合テスト', () => {
|
|
@@ -454,7 +454,7 @@ describe('Post API統合テスト', () => {
|
|
|
454
454
|
})
|
|
455
455
|
|
|
456
456
|
// When: API呼び出し
|
|
457
|
-
const response = await
|
|
457
|
+
const response = await rpc.posts.$get({
|
|
458
458
|
query: { page: '1', limit: '10' }
|
|
459
459
|
})
|
|
460
460
|
|
|
@@ -475,7 +475,7 @@ describe('Post API統合テスト', () => {
|
|
|
475
475
|
})
|
|
476
476
|
|
|
477
477
|
// When: 投稿作成APIを呼び出し
|
|
478
|
-
const response = await
|
|
478
|
+
const response = await rpc.posts.$post({
|
|
479
479
|
json: {
|
|
480
480
|
title: 'New Post',
|
|
481
481
|
content: 'Content',
|
|
@@ -202,6 +202,8 @@ sequenceDiagram
|
|
|
202
202
|
| `scripts` | `scripts/` | ユーティリティスクリプト(`_` プレフィックスは除外) |
|
|
203
203
|
| `env` | `.envrc` | direnv 設定 |
|
|
204
204
|
| `tools` | `.vscode/settings.json` | VS Code 設定 |
|
|
205
|
+
| `root-config` | `package.json`, `.mcp.json` | ルート設定ファイル |
|
|
206
|
+
| `claude-config` | `.claude/settings.json` | Claude Code設定 |
|
|
205
207
|
|
|
206
208
|
**マージ方式:**
|
|
207
209
|
|
|
@@ -213,6 +215,44 @@ sequenceDiagram
|
|
|
213
215
|
| JSON マージ | `.json` 拡張子のファイル | managed/project-private の JSON パス指定に基づきマージ |
|
|
214
216
|
| 3方向マージ | マーカーなしの通常ファイル | base(前回テンプレート)・local・template の3方向差分で自動マージ。コンフリクト時はマーカー付きで出力 |
|
|
215
217
|
|
|
218
|
+
#### JSON マージモード(3モード構成)
|
|
219
|
+
|
|
220
|
+
| モード | 動作 | 設定方法 |
|
|
221
|
+
|--------|------|---------|
|
|
222
|
+
| `managed` | テンプレート値で強制上書き | jsonPaths.managed にパス指定 |
|
|
223
|
+
| `project-private` | 完全除外(テンプレートから追加しない) | jsonPaths["project-private"] にパス指定 |
|
|
224
|
+
| デフォルト | 3方向マージ(base/local/template比較、コンフリクト検出) | 上記以外の全パス |
|
|
225
|
+
|
|
226
|
+
#### JSON ファイルの同期動作(3方向マージ)
|
|
227
|
+
|
|
228
|
+
| 操作 | 結果 |
|
|
229
|
+
|------|------|
|
|
230
|
+
| テンプレートに新キーが追加された(利用者は未変更) | sync時に利用者のファイルに追加される |
|
|
231
|
+
| 利用者が独自キーを追加した | 保持される |
|
|
232
|
+
| 利用者がテンプレート由来のキーを削除(テンプレート側は未変更) | 削除が維持される |
|
|
233
|
+
| 利用者がテンプレート由来のキーを変更(テンプレート側は未変更) | 利用者の変更が保持される |
|
|
234
|
+
| テンプレートがキーを更新(利用者側は未変更) | テンプレートの更新が自動適用される |
|
|
235
|
+
| 両方が同じキーを異なる値に変更 | コンフリクト警告(利用者の値を保持) |
|
|
236
|
+
| project-private 指定のキー | テンプレートから一切追加・変更されない |
|
|
237
|
+
| managed 指定のキー | テンプレート値で常に上書き |
|
|
238
|
+
|
|
239
|
+
#### ファイル別 jsonPaths 設定
|
|
240
|
+
|
|
241
|
+
| ファイル | managed | project-private | 残り |
|
|
242
|
+
|---------|---------|----------------|------|
|
|
243
|
+
| `package.json` | — | name, version, private, workspaces, packageManager, volta | 3方向マージ |
|
|
244
|
+
| `.claude/settings.json` | plansDirectory, includeCoAuthoredBy | — | 3方向マージ |
|
|
245
|
+
| `.vscode/settings.json` | editor.*, eslint.*, prettier.*, [json], [jsonc] | — | 3方向マージ |
|
|
246
|
+
| `.mcp.json` | — | — | 3方向マージ |
|
|
247
|
+
|
|
248
|
+
#### base スナップショット
|
|
249
|
+
|
|
250
|
+
3方向マージには「前回sync時のテンプレート内容」(base)が必要。
|
|
251
|
+
`.einja-sync.json` の `baseContent` フィールドに保存される。
|
|
252
|
+
|
|
253
|
+
- 初回sync(baseなし): ローカル優先 + テンプレートの新規キーのみ追加
|
|
254
|
+
- 2回目以降: base/local/template の3方向比較でマージ
|
|
255
|
+
|
|
216
256
|
#### create-einja-app sync の同期対象カテゴリ
|
|
217
257
|
|
|
218
258
|
| カテゴリ | 対象パターン | デフォルト選択 |
|
|
@@ -92,17 +92,23 @@ project-root/
|
|
|
92
92
|
│ ├── web/ # ユーザー向けWebアプリ
|
|
93
93
|
│ │ ├── src/
|
|
94
94
|
│ │ │ ├── app/ # Next.js App Router
|
|
95
|
+
│ │ │ │ └── api/rpc/ # ドメインベースRPC分割
|
|
96
|
+
│ │ │ │ └── {domain}/[[...route]]/route.ts
|
|
95
97
|
│ │ │ ├── application/ # アプリケーション層(UseCases)⭐
|
|
96
98
|
│ │ │ ├── components/ # Reactコンポーネント
|
|
97
|
-
│ │ │ ├── lib/ #
|
|
99
|
+
│ │ │ ├── lib/ # ライブラリ
|
|
100
|
+
│ │ │ │ └── rpc.ts # ドメインごとのHono RPCクライアント
|
|
98
101
|
│ │ │ └── hooks/ # カスタムフック(Tanstack Query)
|
|
99
102
|
│ │ └── package.json
|
|
100
103
|
│ ├── admin/ # 管理画面
|
|
101
104
|
│ │ ├── src/
|
|
102
105
|
│ │ │ ├── app/
|
|
106
|
+
│ │ │ │ └── api/rpc/ # ドメインベースRPC分割
|
|
107
|
+
│ │ │ │ └── {domain}/[[...route]]/route.ts
|
|
103
108
|
│ │ │ ├── application/ # アプリケーション層(UseCases)⭐
|
|
104
109
|
│ │ │ ├── components/
|
|
105
110
|
│ │ │ ├── lib/
|
|
111
|
+
│ │ │ │ └── rpc.ts # ドメインごとのHono RPCクライアント
|
|
106
112
|
│ │ │ └── hooks/
|
|
107
113
|
│ │ └── package.json
|
|
108
114
|
│ └── cron-worker/ # バッチ処理・定期実行
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
- [メソッドチェーンパターン](#メソッドチェーンパターン)
|
|
16
16
|
- [ミドルウェアと型推論の注意点](#ミドルウェアと型推論の注意点) ⚠️
|
|
17
17
|
- [basePathとHono Clientの関係](#basepathとhono-clientの関係) ⚠️
|
|
18
|
+
- [ドメインベースRPC分割の設計原則](#ドメインベースrpc分割の設計原則)
|
|
18
19
|
2. [Web APIエンドポイント一覧](#2-web-apiエンドポイント一覧)
|
|
19
20
|
3. [Admin APIエンドポイント一覧](#3-admin-apiエンドポイント一覧)
|
|
20
21
|
4. [Cron Worker CLIコマンド](#4-cron-worker-cliコマンド)
|
|
@@ -34,10 +35,51 @@
|
|
|
34
35
|
|
|
35
36
|
Honoは型安全なWebフレームワークで、すべてのNext.js APIルートで使用します。
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
**エントリーポイント**(ドメインベースRPC分割):
|
|
38
39
|
```
|
|
39
|
-
apps/web/app/api/[[...route]]/route.ts # Web API
|
|
40
|
-
apps/admin/app/api/[[...route]]/route.ts # Admin API
|
|
40
|
+
apps/web/app/api/rpc/{domain}/[[...route]]/route.ts # Web API(ドメインごとに分割)
|
|
41
|
+
apps/admin/app/api/rpc/{domain}/[[...route]]/route.ts # Admin API(ドメインごとに分割)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**ドメインroute.tsテンプレート**:
|
|
45
|
+
|
|
46
|
+
各ドメインのroute.tsは以下のパターンで実装します。`basePath`でドメインのフルパスを指定し、`hc<...>("/")`で型ツリーからドメインクライアントを抽出します。
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// apps/web/app/api/rpc/users/[[...route]]/route.ts
|
|
50
|
+
import { userRoutes } from "@web/server/presentation/routes/userRoutes";
|
|
51
|
+
import { Hono } from "hono";
|
|
52
|
+
import { handle } from "hono/vercel";
|
|
53
|
+
|
|
54
|
+
const app = new Hono().basePath("/api/rpc/users");
|
|
55
|
+
const routes = app.route("/", userRoutes);
|
|
56
|
+
|
|
57
|
+
export type UsersAppType = typeof routes;
|
|
58
|
+
|
|
59
|
+
export const GET = handle(app);
|
|
60
|
+
export const POST = handle(app);
|
|
61
|
+
export const PUT = handle(app);
|
|
62
|
+
export const DELETE = handle(app);
|
|
63
|
+
export const PATCH = handle(app);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**RPCクライアントのセットアップ**:
|
|
67
|
+
|
|
68
|
+
> **Note**: 以下は複数ドメインを追加した場合の完成形の例です。現在のテンプレートでは `users` ドメインのみ実装されています。
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// apps/web/src/lib/api/rpc.ts
|
|
72
|
+
import type { AuthAppType } from "@/app/api/rpc/auth/[[...route]]/route";
|
|
73
|
+
import type { PostsAppType } from "@/app/api/rpc/posts/[[...route]]/route";
|
|
74
|
+
import { hc } from "hono/client";
|
|
75
|
+
|
|
76
|
+
const authClient = hc<AuthAppType>("/");
|
|
77
|
+
const postsClient = hc<PostsAppType>("/");
|
|
78
|
+
|
|
79
|
+
export const rpc = {
|
|
80
|
+
auth: authClient.api.rpc.auth,
|
|
81
|
+
posts: postsClient.api.rpc.posts,
|
|
82
|
+
} as const;
|
|
41
83
|
```
|
|
42
84
|
|
|
43
85
|
**ルート定義の配置**:
|
|
@@ -59,45 +101,70 @@ Hono Clientの型推論は `typeof app` から型情報を抽出します。メ
|
|
|
59
101
|
const app = new Hono()
|
|
60
102
|
app.get('/posts', handler1) // 返り値が破棄される
|
|
61
103
|
app.post('/posts', handler2) // 返り値が破棄される
|
|
62
|
-
export type
|
|
104
|
+
export type PostsAppType = typeof app // ルート情報が不完全
|
|
63
105
|
|
|
64
106
|
// ✅ OK: メソッドチェーン - 完全な型推論
|
|
65
107
|
const app = new Hono()
|
|
66
108
|
.get('/posts', handler1)
|
|
67
109
|
.post('/posts', handler2)
|
|
68
|
-
export type
|
|
110
|
+
export type PostsAppType = typeof app // 全ルート情報を含む
|
|
69
111
|
```
|
|
70
112
|
|
|
71
|
-
メソッドチェーンにより、Hono Client (`hc<
|
|
113
|
+
メソッドチェーンにより、Hono Client (`hc<PostsAppType>`) でエンドツーエンドの型安全なAPI呼び出しが実現できます。
|
|
72
114
|
|
|
73
115
|
### ミドルウェアと型推論の注意点
|
|
74
116
|
|
|
75
|
-
**サブルート内で`.use()
|
|
117
|
+
**サブルート内で`.use()`を使うと型推論が壊れる。各ドメインroute.tsのアプリ側で適用すること。**
|
|
118
|
+
|
|
119
|
+
ドメインベースRPC分割では、各ドメインのroute.tsが独立したHonoアプリを持つため、ミドルウェアはそのアプリに直接適用します。
|
|
76
120
|
|
|
77
121
|
```typescript
|
|
78
122
|
// ❌ NG: サブルート内で.use() → 型が ClientRequest<{}> になる
|
|
79
|
-
export const
|
|
80
|
-
.use("*",
|
|
81
|
-
.
|
|
123
|
+
export const postRoutes = new Hono()
|
|
124
|
+
.use("*", authMiddleware)
|
|
125
|
+
.post("/", handler)
|
|
82
126
|
|
|
83
|
-
// ✅ OK:
|
|
127
|
+
// ✅ OK: ドメインroute.tsのアプリ側で.use()を適用
|
|
128
|
+
// apps/web/app/api/rpc/posts/[[...route]]/route.ts
|
|
84
129
|
const app = new Hono()
|
|
85
|
-
.basePath("/api")
|
|
86
|
-
.use("
|
|
87
|
-
|
|
130
|
+
.basePath("/api/rpc/posts")
|
|
131
|
+
.use("/*", authMiddleware); // ← 認証必要なドメインではここで適用
|
|
132
|
+
const routes = app.route("/", postRoutes);
|
|
133
|
+
export type PostsAppType = typeof routes;
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**ドメインごとのミドルウェア適用パターン**:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// 認証不要のドメイン(auth等)
|
|
140
|
+
const app = new Hono().basePath("/api/rpc/auth");
|
|
141
|
+
const routes = app.route("/", authRoutes);
|
|
142
|
+
export type AuthAppType = typeof routes;
|
|
143
|
+
|
|
144
|
+
// 認証必要のドメイン(posts等)
|
|
145
|
+
const app = new Hono()
|
|
146
|
+
.basePath("/api/rpc/posts")
|
|
147
|
+
.use("/*", authMiddleware);
|
|
148
|
+
const routes = app.route("/", postRoutes);
|
|
149
|
+
export type PostsAppType = typeof routes;
|
|
88
150
|
```
|
|
89
151
|
|
|
90
152
|
### basePathとHono Clientの関係
|
|
91
153
|
|
|
92
|
-
|
|
154
|
+
ドメインベースRPC分割では`basePath`で**ドメインのフルパスを指定**する。`hono/vercel`の`handle()`関数は完全なURLパスをHonoに渡すため、Honoがルートを正しくマッチするには`basePath`が必要。
|
|
93
155
|
|
|
94
156
|
```typescript
|
|
95
|
-
// サーバー: basePath
|
|
96
|
-
const app = new Hono().basePath("/api/rpc
|
|
157
|
+
// サーバー: basePathでドメインのフルパスを指定
|
|
158
|
+
const app = new Hono().basePath("/api/rpc/users");
|
|
159
|
+
const routes = app.route("/", userRoutes);
|
|
160
|
+
export type UsersAppType = typeof routes;
|
|
97
161
|
|
|
98
|
-
// クライアント:
|
|
99
|
-
|
|
100
|
-
|
|
162
|
+
// クライアント: hc("/")で型ツリーを構築し、ドメイン部分を抽出
|
|
163
|
+
import { hc } from "hono/client";
|
|
164
|
+
const client = hc<UsersAppType>("/");
|
|
165
|
+
const usersRpc = client.api.rpc.users;
|
|
166
|
+
|
|
167
|
+
usersRpc.$get() // ✅ OK(/api/rpc/users にリクエスト)
|
|
101
168
|
```
|
|
102
169
|
|
|
103
170
|
**使用例**:
|
|
@@ -128,6 +195,30 @@ app
|
|
|
128
195
|
export default app
|
|
129
196
|
```
|
|
130
197
|
|
|
198
|
+
### ドメインベースRPC分割の設計原則
|
|
199
|
+
|
|
200
|
+
#### なぜドメインで分割するか
|
|
201
|
+
|
|
202
|
+
1. **Vercel Function独立**: 各ドメインが独立したServerless Functionとしてデプロイされるため、障害の影響範囲が限定される
|
|
203
|
+
2. **コールドスタート改善**: 各Functionのバンドルサイズが小さくなり、コールドスタート時間が短縮される
|
|
204
|
+
3. **スケーラビリティ**: トラフィックの多いドメイン(例: posts)だけが独立してスケールできる
|
|
205
|
+
|
|
206
|
+
#### ドメイングループ設計の基準
|
|
207
|
+
|
|
208
|
+
ドメインの分割は**依存の重さ・頻度**で分類する。
|
|
209
|
+
|
|
210
|
+
| 基準 | 説明 | 例 |
|
|
211
|
+
|------|------|-----|
|
|
212
|
+
| 依存の独立性 | 外部サービスやDB接続が異なる | auth(認証サービス依存)vs posts(DB依存) |
|
|
213
|
+
| リクエスト頻度 | 高頻度ドメインを分離して他に影響させない | analytics(低頻度)vs posts(高頻度) |
|
|
214
|
+
| ミドルウェアの違い | 認証要否など共通処理が異なる | auth(認証不要)vs users(認証必要) |
|
|
215
|
+
|
|
216
|
+
#### 新ドメイン追加手順
|
|
217
|
+
|
|
218
|
+
1. **route.ts作成**: `app/api/rpc/{domain}/[[...route]]/route.ts` にドメインroute.tsテンプレートを配置
|
|
219
|
+
2. **rpc.tsに追加**: `lib/api/rpc.ts` の `rpc` オブジェクトに新ドメインのクライアントを追加
|
|
220
|
+
3. **フック作成**: 必要に応じて `hooks/api/use{Domain}*.ts` にTanstack Queryフックを作成
|
|
221
|
+
|
|
131
222
|
### zValidatorの統合
|
|
132
223
|
|
|
133
224
|
**必須**: すべてのリクエストボディとクエリパラメータは、zValidatorでバリデーションを行う。
|
|
@@ -176,23 +267,23 @@ async (c) => {
|
|
|
176
267
|
|---------|---------------|------|-----------|-----------|------|
|
|
177
268
|
| GET | `/api/health` | システム稼働確認 | - | `{status: "ok"}` | 不要 |
|
|
178
269
|
|
|
179
|
-
###
|
|
270
|
+
### 認証エンドポイント(ドメイン: auth)
|
|
180
271
|
|
|
181
272
|
| メソッド | エンドポイント | 説明 | リクエスト | レスポンス | 認証 |
|
|
182
273
|
|---------|---------------|------|-----------|-----------|------|
|
|
183
|
-
| POST | `/api/auth/login` | ユーザーログイン | `{email, password}` | `{token, user}` | 不要 |
|
|
184
|
-
| POST | `/api/auth/logout` | ログアウト | - | `{success: true}` | 必要 |
|
|
185
|
-
| GET | `/api/auth/session` | セッション確認 | - | `{user}` | 必要 |
|
|
274
|
+
| POST | `/api/rpc/auth/login` | ユーザーログイン | `{email, password}` | `{token, user}` | 不要 |
|
|
275
|
+
| POST | `/api/rpc/auth/logout` | ログアウト | - | `{success: true}` | 必要 |
|
|
276
|
+
| GET | `/api/rpc/auth/session` | セッション確認 | - | `{user}` | 必要 |
|
|
186
277
|
|
|
187
|
-
###
|
|
278
|
+
### 投稿エンドポイント(ドメイン: posts)
|
|
188
279
|
|
|
189
280
|
| メソッド | エンドポイント | 説明 | リクエスト | レスポンス | 認証 |
|
|
190
281
|
|---------|---------------|------|-----------|-----------|------|
|
|
191
|
-
| GET | `/api/posts` | 投稿一覧取得 | `?page=1&limit=10` | `{posts[], total}` | オプション |
|
|
192
|
-
| POST | `/api/posts` | 投稿作成 | `{title, content, status?}` | `{post}` | 必要 |
|
|
193
|
-
| GET | `/api/posts/:id` | 投稿詳細取得 | - | `{post}` | オプション |
|
|
194
|
-
| PUT | `/api/posts/:id` | 投稿更新 | `{title?, content?, status?}` | `{post}` | 必要 |
|
|
195
|
-
| DELETE | `/api/posts/:id` | 投稿削除 | - | `{success: true}` | 必要 |
|
|
282
|
+
| GET | `/api/rpc/posts` | 投稿一覧取得 | `?page=1&limit=10` | `{posts[], total}` | オプション |
|
|
283
|
+
| POST | `/api/rpc/posts` | 投稿作成 | `{title, content, status?}` | `{post}` | 必要 |
|
|
284
|
+
| GET | `/api/rpc/posts/:id` | 投稿詳細取得 | - | `{post}` | オプション |
|
|
285
|
+
| PUT | `/api/rpc/posts/:id` | 投稿更新 | `{title?, content?, status?}` | `{post}` | 必要 |
|
|
286
|
+
| DELETE | `/api/rpc/posts/:id` | 投稿削除 | - | `{success: true}` | 必要 |
|
|
196
287
|
|
|
197
288
|
**ページネーション設計**:
|
|
198
289
|
- `page`: ページ番号(デフォルト: 1)
|
|
@@ -214,27 +305,27 @@ async (c) => {
|
|
|
214
305
|
|---------|---------------|------|-----------|-----------|------|
|
|
215
306
|
| GET | `/api/health` | システム稼働確認 | - | `{status: "ok"}` | 不要 |
|
|
216
307
|
|
|
217
|
-
###
|
|
308
|
+
### ユーザー管理(ドメイン: users)
|
|
218
309
|
|
|
219
310
|
| メソッド | エンドポイント | 説明 | リクエスト | レスポンス | 認証 |
|
|
220
311
|
|---------|---------------|------|-----------|-----------|------|
|
|
221
|
-
| GET | `/api/
|
|
222
|
-
| GET | `/api/
|
|
223
|
-
| PUT | `/api/
|
|
224
|
-
| DELETE | `/api/
|
|
312
|
+
| GET | `/api/rpc/users` | ユーザー一覧取得 | `?page=1&limit=20` | `{users[], total}` | 管理者 |
|
|
313
|
+
| GET | `/api/rpc/users/:id` | ユーザー詳細取得 | - | `{user}` | 管理者 |
|
|
314
|
+
| PUT | `/api/rpc/users/:id` | ユーザー情報更新 | `{name?, email?}` | `{user}` | 管理者 |
|
|
315
|
+
| DELETE | `/api/rpc/users/:id` | ユーザー削除 | - | `{success: true}` | 管理者 |
|
|
225
316
|
|
|
226
|
-
###
|
|
317
|
+
### 投稿管理(ドメイン: posts)
|
|
227
318
|
|
|
228
319
|
| メソッド | エンドポイント | 説明 | リクエスト | レスポンス | 認証 |
|
|
229
320
|
|---------|---------------|------|-----------|-----------|------|
|
|
230
|
-
| GET | `/api/
|
|
231
|
-
| PUT | `/api/
|
|
321
|
+
| GET | `/api/rpc/posts` | 全投稿一覧取得 | `?status=all&page=1` | `{posts[], total}` | 管理者 |
|
|
322
|
+
| PUT | `/api/rpc/posts/:id/status` | 投稿ステータス変更 | `{status}` | `{post}` | 管理者 |
|
|
232
323
|
|
|
233
|
-
###
|
|
324
|
+
### 分析(ドメイン: analytics)
|
|
234
325
|
|
|
235
326
|
| メソッド | エンドポイント | 説明 | リクエスト | レスポンス | 認証 |
|
|
236
327
|
|---------|---------------|------|-----------|-----------|------|
|
|
237
|
-
| GET | `/api/
|
|
328
|
+
| GET | `/api/rpc/analytics` | システム統計取得 | `?from=&to=` | `{stats}` | 管理者 |
|
|
238
329
|
|
|
239
330
|
**管理者認証**:
|
|
240
331
|
- すべてのAdmin APIは、管理者権限(`role='admin'`)のチェックが必要
|
|
@@ -567,13 +658,13 @@ Hono Client + Tanstack Queryでは、`parseResponse`関数を使用してレス
|
|
|
567
658
|
import { useQuery } from "@tanstack/react-query";
|
|
568
659
|
import { parseResponse } from "@/lib/api/parse-response";
|
|
569
660
|
import { paginatedPostListSchema } from "@/shared/schemas/post";
|
|
570
|
-
import {
|
|
661
|
+
import { rpc } from "@/lib/api/rpc";
|
|
571
662
|
|
|
572
663
|
export function usePostList(page: number, limit: number) {
|
|
573
664
|
return useQuery({
|
|
574
665
|
queryKey: ["posts", page, limit],
|
|
575
666
|
queryFn: async () => {
|
|
576
|
-
const response = await
|
|
667
|
+
const response = await rpc.posts.$get({
|
|
577
668
|
query: { page: String(page), limit: String(limit) },
|
|
578
669
|
});
|
|
579
670
|
return parseResponse(response, paginatedPostListSchema);
|
|
@@ -651,26 +742,45 @@ export const adminMiddleware = createMiddleware(async (c, next) => {
|
|
|
651
742
|
})
|
|
652
743
|
```
|
|
653
744
|
|
|
654
|
-
###
|
|
745
|
+
### ミドルウェアの適用(ドメインベースRPC分割)
|
|
746
|
+
|
|
747
|
+
ドメインベースRPC分割では、各ドメインのroute.tsが独立したHonoアプリを持つため、ミドルウェアはドメインごとに個別に適用します。
|
|
655
748
|
|
|
656
749
|
```typescript
|
|
657
|
-
|
|
658
|
-
import {
|
|
750
|
+
// 認証不要のドメイン: apps/web/app/api/rpc/auth/[[...route]]/route.ts
|
|
751
|
+
import { authRoutes } from "@web/server/presentation/routes/authRoutes";
|
|
752
|
+
import { Hono } from "hono";
|
|
753
|
+
import { handle } from "hono/vercel";
|
|
659
754
|
|
|
660
|
-
const app = new Hono()
|
|
755
|
+
const app = new Hono().basePath("/api/rpc/auth");
|
|
756
|
+
const routes = app.route("/", authRoutes);
|
|
757
|
+
export type AuthAppType = typeof routes;
|
|
661
758
|
|
|
662
|
-
|
|
663
|
-
|
|
759
|
+
export const GET = handle(app);
|
|
760
|
+
export const POST = handle(app);
|
|
761
|
+
```
|
|
664
762
|
|
|
665
|
-
|
|
666
|
-
app
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
}
|
|
763
|
+
```typescript
|
|
764
|
+
// 認証必要のドメイン: apps/web/app/api/rpc/posts/[[...route]]/route.ts
|
|
765
|
+
import { postRoutes } from "@web/server/presentation/routes/postRoutes";
|
|
766
|
+
import { authMiddleware } from "@/server/middleware/auth";
|
|
767
|
+
import { Hono } from "hono";
|
|
768
|
+
import { handle } from "hono/vercel";
|
|
769
|
+
|
|
770
|
+
const app = new Hono()
|
|
771
|
+
.basePath("/api/rpc/posts")
|
|
772
|
+
.use("/*", authMiddleware); // ← ドメインroute.tsで認証ミドルウェアを適用
|
|
773
|
+
const routes = app.route("/", postRoutes);
|
|
774
|
+
export type PostsAppType = typeof routes;
|
|
775
|
+
|
|
776
|
+
export const GET = handle(app);
|
|
777
|
+
export const POST = handle(app);
|
|
778
|
+
export const PUT = handle(app);
|
|
779
|
+
export const DELETE = handle(app);
|
|
780
|
+
export const PATCH = handle(app);
|
|
671
781
|
```
|
|
672
782
|
|
|
673
|
-
**⚠️ 重要**:
|
|
783
|
+
**⚠️ 重要**: サブルート(postRoutes等)内で`.use()`を使用するとHono RPC型推論が壊れます。ミドルウェアは必ずドメインroute.tsのアプリ側で適用してください。
|
|
674
784
|
詳細は[ミドルウェアと型推論の注意点](#ミドルウェアと型推論の注意点)を参照してください。
|
|
675
785
|
|
|
676
786
|
---
|
|
@@ -789,24 +899,46 @@ app.post(
|
|
|
789
899
|
export default app
|
|
790
900
|
```
|
|
791
901
|
|
|
792
|
-
|
|
902
|
+
**エントリーポイント(ドメインroute.ts)**:
|
|
793
903
|
|
|
794
904
|
```typescript
|
|
795
|
-
// apps/web/
|
|
796
|
-
import {
|
|
797
|
-
import {
|
|
798
|
-
import {
|
|
905
|
+
// apps/web/app/api/rpc/posts/[[...route]]/route.ts
|
|
906
|
+
import { postRoutes } from "@web/server/presentation/routes/postRoutes";
|
|
907
|
+
import { authMiddleware } from "@/server/middleware/auth";
|
|
908
|
+
import { Hono } from "hono";
|
|
909
|
+
import { handle } from "hono/vercel";
|
|
799
910
|
|
|
800
|
-
const app = new Hono()
|
|
911
|
+
const app = new Hono()
|
|
912
|
+
.basePath("/api/rpc/posts")
|
|
913
|
+
.use("/*", authMiddleware);
|
|
914
|
+
const routes = app.route("/", postRoutes);
|
|
915
|
+
|
|
916
|
+
export type PostsAppType = typeof routes;
|
|
801
917
|
|
|
802
|
-
const
|
|
918
|
+
export const GET = handle(app);
|
|
919
|
+
export const POST = handle(app);
|
|
920
|
+
export const PUT = handle(app);
|
|
921
|
+
export const DELETE = handle(app);
|
|
922
|
+
export const PATCH = handle(app);
|
|
923
|
+
```
|
|
803
924
|
|
|
804
|
-
|
|
805
|
-
export const POST = handle(app)
|
|
806
|
-
export const PUT = handle(app)
|
|
807
|
-
export const DELETE = handle(app)
|
|
925
|
+
**RPCクライアント**:
|
|
808
926
|
|
|
809
|
-
|
|
927
|
+
> **Note**: 以下は複数ドメインを追加した場合の完成形の例です。現在のテンプレートでは `users` ドメインのみ実装されています。
|
|
928
|
+
|
|
929
|
+
```typescript
|
|
930
|
+
// apps/web/src/lib/api/rpc.ts
|
|
931
|
+
import type { AuthAppType } from "@/app/api/rpc/auth/[[...route]]/route";
|
|
932
|
+
import type { PostsAppType } from "@/app/api/rpc/posts/[[...route]]/route";
|
|
933
|
+
import { hc } from "hono/client";
|
|
934
|
+
|
|
935
|
+
const authClient = hc<AuthAppType>("/");
|
|
936
|
+
const postsClient = hc<PostsAppType>("/");
|
|
937
|
+
|
|
938
|
+
export const rpc = {
|
|
939
|
+
auth: authClient.api.rpc.auth,
|
|
940
|
+
posts: postsClient.api.rpc.posts,
|
|
941
|
+
} as const;
|
|
810
942
|
```
|
|
811
943
|
|
|
812
944
|
**フロントエンドでの使用**:
|
|
@@ -194,7 +194,7 @@ graph TD
|
|
|
194
194
|
|
|
195
195
|
#### 📕 Presentation層(API Routes)
|
|
196
196
|
|
|
197
|
-
**配置**: `apps/web/src/app/api/`, `apps/admin/src/app/api/`
|
|
197
|
+
**配置**: `apps/web/src/app/api/rpc/{domain}/`, `apps/admin/src/app/api/rpc/{domain}/`
|
|
198
198
|
|
|
199
199
|
**責務**:
|
|
200
200
|
- HTTPリクエスト/レスポンスの処理
|
|
@@ -204,28 +204,24 @@ graph TD
|
|
|
204
204
|
|
|
205
205
|
**技術**: Hono、zValidator、Zod
|
|
206
206
|
|
|
207
|
+
**エントリーポイント**: `/api/rpc/{domain}/[[...route]]/route.ts` (ドメインベースRPC分割)
|
|
208
|
+
|
|
207
209
|
**実装例**:
|
|
208
210
|
```typescript
|
|
209
|
-
// apps/web/src/app/api/posts/route.ts
|
|
211
|
+
// apps/web/src/app/api/rpc/posts/[[...route]]/route.ts
|
|
212
|
+
import { postRoutes } from "@web/server/presentation/routes/postRoutes"
|
|
210
213
|
import { Hono } from "hono"
|
|
211
|
-
import {
|
|
212
|
-
import { postSchema } from "@repo/server-core/domain/validators/post"
|
|
213
|
-
import { postUseCases } from "@/application/use-cases/PostUseCases" // アプリ内のApplication層
|
|
214
|
+
import { handle } from "hono/vercel"
|
|
214
215
|
|
|
215
216
|
const app = new Hono()
|
|
216
|
-
.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (!result.isSuccess) {
|
|
221
|
-
return c.json({ error: result.error.message }, result.error.statusCode)
|
|
222
|
-
}
|
|
217
|
+
.basePath("/api/rpc/posts")
|
|
218
|
+
.use("/*", authMiddleware)
|
|
219
|
+
const routes = app.route("/", postRoutes)
|
|
223
220
|
|
|
224
|
-
|
|
225
|
-
})
|
|
221
|
+
export type PostsAppType = typeof routes
|
|
226
222
|
|
|
227
|
-
export const GET = app
|
|
228
|
-
export const POST = app
|
|
223
|
+
export const GET = handle(app)
|
|
224
|
+
export const POST = handle(app)
|
|
229
225
|
```
|
|
230
226
|
|
|
231
227
|
---
|