AI駆動開発
コードを殆ど書かずに実装してみた。成果物は これ 。
使っているものは以下の通り。
- sqlc: database 処理のコードを自動生成
- TypeSpec: OpenAPI を自動生成
- ogen: OpenAPI から handler 処理のコードを自動生成
- Orval: OpenAPI からフロントのクライアントコードを自動生成
実際のバックエンドの実装の流れはこんな感じ。
- step1: database を定義、sqlc を使ってデータベースロジックを自動生成
- step2: エンドポイントを定義、TypeSpec と ogen を使って handler のロジックを自動生成
- step3: domain ロジックを実装
- step4 application ロジックを実装
- step5: infra ロジックを実装
このやりかたであれば、スキーマ定義と、エンドポイントの定義さえやれば、domain ロジックと、application ロジックの実装にのみ集中できる。
今回は、このやりかたをスラッシュコマンドに落とし込んだ。
| カスタムスラッシュコマンド | 内容 |
|---|---|
| api-gen-spec | APIリソースのTypeSpec定義とSQL定義を生成 |
| api-gen-domain | Domain層を生成 |
| api-gen-application | Application層(DTO、Usecase、テスト)を生成 |
| api-gen-infra | Infrastructure層(Repository実装、Handler)を生成 |
| api-check | APIエンドポイントの動作確認 |
api-gen-spec
> /api-gen-spec comments id:INTEGER,post_id:INTEGER,body:TEXT,commented_at:TIMESTAMP
⏺ User answered Claude's questions:
⎿ · commentsテーブルのidはauto incrementで自動生成しますか? → はい(推奨)
· post_idにFOREIGN KEY制約を付けますか? → いいえ
· SELECT用クエリはどれが必要ですか? → 基本CRUDだが全件検索のキーはpost_idにして欲しい
· APIのパスはどうしますか? → /posts/{postId}/comments
了解しました。以下の仕様で生成します:
- id: auto increment
- 外部キー制約なし
- SELECTクエリ: id指定の単一取得 + post_id指定の複数取得
- APIパス: /posts/{postId}/comments
⏺ 生成完了
以下のファイルを更新しました:
1. sqlc/schema.sql
CREATE TABLE IF NOT EXISTS comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
post_id INTEGER NOT NULL,
body TEXT NOT NULL,
commented_at TIMESTAMP NOT NULL
);
2. sqlc/query.sql
- InsertComment - コメント作成
- SelectComment - ID指定で1件取得
- SelectCommentsByPostId - post_id指定で複数取得
- UpdateComment - コメント更新
- DeleteComment - コメント削除
3. main.tsp
- Comment, CommentList モデル
- CreateCommentRequest, UpdateCommentRequest リクエストモデル
- /posts/{postId}/comments エンドポイント(CRUD)
api-gen-domain
> /api-gen-domain
⏺ パターンを確認しました。schemaからcommentsテーブルのフィールドを把握しました:
- id: INTEGER (auto increment)
- post_id: INTEGER
- body: TEXT
- commented_at: TIMESTAMP
bodyフィールドの制約について確認が必要です。
⏺ User answered Claude's questions:
⎿ · CommentBodyの文字数制約はどうしますか? → 1-500文字
· RepositoryのFindAllはどのようにしますか? → FindAll + FindByPostID
⏺ 了解しました。以下の仕様で生成します:
- body: 1-500文字
- Repository: FindAll + FindByPostID
> Makefileに今回追加したrepositoryを追加したい
⏺ Update(Makefile)
⎿ Added 4 lines
5 -source=./domain/repository/post_repository.go \
6 -destination=./testing/mock/post_repository.go \
7 -package=mock
8 + $(MOCK_GEN) \
9 + -source=./domain/repository/comment_repository.go \
10 + -destination=./testing/mock/comment_repository.go \
11 + -package=mock
12
13 test:
14 go test ./...
⏺ Makefileに追加しました。make mockを実行すると両方のrepositoryのmockが生成されます。
! make mock
⎿ go run -mod=mod go.uber.org/mock/mockgen@latest \
-source=./domain/repository/post_repository.go \
-destination=./testing/mock/post_repository.go \
… +5 lines (ctrl+o to expand)
api-gen-application
> /api-gen-application
⏺ パターンを確認しました。CommentのAPI設計が/posts/{postId}/commentsなので、Listの検索はFindByPostIDを使用し、postIDを引数に取る必要があります。
⏺ 生成完了
全てのテストがパスしました。以下のファイルを生成しました:
DTO
- application/dto/comment_dto.go
- CreateCommentInput/Output
- GetCommentInput/Output
- CommentItem, ListCommentsInput/Output
- UpdateCommentInput/Output
- DeleteCommentInput
Usecase
- application/usecase/comment/create_comment.go
- application/usecase/comment/get_comment.go
- application/usecase/comment/list_comments.go - FindByPostIDを使用
- application/usecase/comment/update_comment.go
- application/usecase/comment/delete_comment.go
Usecase Tests
- application/usecase/comment/create_comment_test.go
- application/usecase/comment/get_comment_test.go
- application/usecase/comment/list_comments_test.go
- application/usecase/comment/update_comment_test.go
- application/usecase/comment/delete_comment_test.go
api-gen-infra
> /api-gen-infra
⏺ sqlcとogenがComment用に生成されていません。まず以下のコマンドを実行する必要があります:
# sqlc生成
make sqlc.generate
# TypeSpecコンパイル
make tsp.compile
# ogen生成
make ogen
これらを実行してから、再度 /api-gen-infra を実行してください。
先に実行しますか?
> yes
⏺ 生成されたコードを確認しました。Infrastructure層を生成します。
⏺ 生成完了
全てのテストがパスしました。以下のファイルを生成・更新しました:
生成したファイル
- infra/database/comment_repository.go - CommentRepository実装
- Save, FindByID, FindAll, FindByPostID, Update, Delete
- infra/handler/comment_handler.go - CommentHandler
- CommentsCreate, CommentsRead, CommentsList, CommentsUpdate, CommentsDelete
- infra/handler/handler_test.go - 統合Handlerのテスト
更新したファイル
- infra/handler/handler.go - 統合HandlerにCommentHandlerを追加
- infra/handler/post_handler.go - 不要なインターフェース検証とNewErrorを削除
- cmd/main.go - Comment関連の初期化コードを追加
削除したファイル
- infra/handler/post_handler_test.go - handler_test.goに統合
実行したコード生成
- make sqlc.generate - SQLCコード生成
- make tsp.compile - TypeSpecコンパイル
- make ogen - OpenAPIからGoコード生成
api-check
> /api-check postとcommentの動作を一通り確認して
⏺ postとcommentのAPI動作を一通り確認します。
⏺ Bash(curl -s http://localhost:8080/posts | jq .)
⎿ {
"items": []
}
⏺ 投稿一覧は空です。投稿を作成してテストを進めます。
⏺ Bash(curl -s -X POST http://localhost:8080/posts \
-H "Content-Type: application/json" \…)
⎿ curl: option : blank argument where content is expected
curl: try 'curl --help' or 'curl --manual' for more information
⏺ Bash(curl -s -X POST http://localhost:8080/posts -H "Content-Type: application/json" -d '{"title":"テスト投稿","content":"これはテスト内容です"}'
| jq .)
⎿ {
"id": 1,
"title": "テスト投稿",
… +3 lines (ctrl+o to expand)
⏺ 投稿作成成功。ID=1で作成されました。
⏺ Bash(curl -s http://localhost:8080/posts/1 | jq .)
⎿ {
"id": 1,
"title": "テスト投稿",
… +3 lines (ctrl+o to expand)
⏺ 投稿詳細取得成功。
⏺ Bash(curl -s -X PUT http://localhost:8080/posts/1 -H "Content-Type: application/json" -d '{"title":"更新後タイトル","content":"更新後の内容"}' |
jq .)
⎿ {
"id": 1,
"title": "更新後タイトル",
… +3 lines (ctrl+o to expand)
⏺ 投稿更新成功。タイトルと内容が更新されました。
⏺ 投稿削除はコメントAPIを先にテストしてから行います(投稿を削除するとコメントテストができないため)。
⏺ Bash(curl -s -X POST http://localhost:8080/posts/1/comments -H "Content-Type: application/json" -d '{"body":"これはテストコメントです"}' | jq .)
⎿ {
"id": 1,
"postId": 1,
… +3 lines (ctrl+o to expand)
⏺ コメント作成成功。ID=1で作成されました。
⏺ Bash(curl -s http://localhost:8080/posts/1/comments | jq .)
⎿ {
"items": [
{
… +7 lines (ctrl+o to expand)
⏺ コメント一覧取得成功。1件のコメントが返されました。
⏺ Bash(curl -s http://localhost:8080/posts/1/comments/1 | jq .)
⎿ {
"id": 1,
"postId": 1,
… +3 lines (ctrl+o to expand)
⏺ コメント詳細取得成功。
⏺ Bash(curl -s -X PUT http://localhost:8080/posts/1/comments/1 -H "Content-Type: application/json" -d '{"body":"更新後のコメント"}' | jq .)
⎿ {
"id": 1,
"postId": 1,
… +3 lines (ctrl+o to expand)
⏺ コメント更新成功。
⏺ Bash(curl -s -X DELETE http://localhost:8080/posts/1/comments/1 -w "\nHTTP Status: %{http_code}\n")
⎿ HTTP Status: 204
⏺ コメント削除成功(204 No Content)。削除後、コメント一覧を確認します。
⏺ Bash(curl -s http://localhost:8080/posts/1/comments | jq .)
⎿ {
"items": []
}
⏺ コメントが正常に削除されました。最後に投稿削除を確認します。
⏺ Bash(curl -s -X DELETE http://localhost:8080/posts/1 -w "\nHTTP Status: %{http_code}\n")
⎿ HTTP Status: 204
⏺ Bash(curl -s http://localhost:8080/posts | jq .)
⎿ {
"items": []
}
⏺ API動作確認結果
すべてのAPIエンドポイントが正常に動作しています。
投稿API (Posts)
| エンドポイント | メソッド | ステータス | 結果 |
|----------------|----------|------------|------------------------|
| /posts | GET | 200 | 一覧取得成功(空配列) |
| /posts | POST | 200 | 作成成功(ID=1) |
| /posts/1 | GET | 200 | 詳細取得成功 |
| /posts/1 | PUT | 200 | 更新成功 |
| /posts/1 | DELETE | 204 | 削除成功 |
コメントAPI (Comments)
| エンドポイント | メソッド | ステータス | 結果 |
|---------------------|----------|------------|------------------|
| /posts/1/comments | POST | 200 | 作成成功(ID=1) |
| /posts/1/comments | GET | 200 | 一覧取得成功 |
| /posts/1/comments/1 | GET | 200 | 詳細取得成功 |
| /posts/1/comments/1 | PUT | 200 | 更新成功 |
| /posts/1/comments/1 | DELETE | 204 | 削除成功 |
すべてのCRUD操作が期待通りに動作し、レスポンスも適切なJSON形式で返却されています。
web
web に関してはすべて AI で作った。
> @tsp-output/schema/openapi.yamlを読んで、postとcommentができるweb画面をweb配下に実装してください。
また https://github.com/orval-labs/orval を使ってください。
⏺ プランを更新しました。
実装計画まとめ
技術スタック:
- Orval - OpenAPI仕様からAPIクライアント自動生成
- TanStack Query - useQuery/useMutation hooks
主な手順:
1. @tanstack/react-query と orval をインストール
2. orval.config.ts を作成してコード生成設定
3. bun run orval でAPIクライアント・型を自動生成
4. APIプロキシを設定(/api/* → localhost:8080/*)
5. 生成されたhooksを使用してBBSコンポーネントを実装
6. 既存のAPITester/ロゴを削除してBBSに置き換え
準備ができました。
⏺ User approved Claude's plan
⎿ Plan saved to: ~/.claude/plans/gleaming-finding-sunrise.md · /plan to edit
BBS Web画面 実装計画
概要
OpenAPI仕様に基づき、Post(投稿)とComment(コメント)機能を持つBBS画面を実装する。
Orval + TanStack Query を使用してAPIクライアントを自動生成する。
API仕様
- GET /posts - 投稿一覧取得
- POST /posts - 投稿作成 (title: 1-30文字, content: 1-255文字)
- GET /posts/{id} - 投稿詳細取得
- GET /posts/{postId}/comments - コメント一覧取得
- POST /posts/{postId}/comments - コメント作成 (body: 1文字以上)
ファイル構成
web/
├── orval.config.ts # Orval設定ファイル
├── src/
│ ├── index.ts # APIプロキシ追加
│ ├── App.tsx # BBSをメインに変更
│ ├── frontend.tsx # QueryClientProvider追加
│ ├── index.css # BBSスタイル追加
│ ├── generated/ # Orval自動生成(git管理対象外)
│ │ ├── api.ts # APIクライアント・hooks
│ │ └── model/ # 型定義
│ └── components/
│ ├── BBS.tsx # メインBBSコンポーネント
│ ├── PostList.tsx # 投稿一覧
│ ├── PostItem.tsx # 投稿アイテム
│ ├── PostForm.tsx # 投稿フォーム
│ ├── PostDetail.tsx # 投稿詳細+コメント
│ ├── CommentList.tsx # コメント一覧
│ ├── CommentItem.tsx # コメントアイテム
│ └── CommentForm.tsx # コメントフォーム
実装手順
Step 1: 依存パッケージのインストール
cd web
bun add @tanstack/react-query
bun add -D orval
Step 2: Orval設定ファイルの作成
web/orval.config.ts
import { defineConfig } from 'orval';
export default defineConfig({
bbs: {
input: '../tsp-output/schema/openapi.yaml',
output: {
mode: 'single',
target: './src/generated/api.ts',
schemas: './src/generated/model',
client: 'react-query',
baseUrl: '/api',
override: {
mutator: {
path: './src/api/fetcher.ts',
name: 'customFetch',
},
},
},
},
});
Step 3: カスタムfetcherの作成
web/src/api/fetcher.ts
- baseURLを /api に設定(プロキシ経由)
Step 4: Orvalでコード生成
bun run orval
生成されるもの:
- usePostsList() - 投稿一覧取得hook
- usePostsCreate() - 投稿作成mutation
- usePostsRead(id) - 投稿詳細取得hook
- useCommentsList(postId) - コメント一覧取得hook
- useCommentsCreate(postId) - コメント作成mutation
- 型定義(Post, Comment, CreatePostRequest等)
Step 5: APIプロキシの設定
web/src/index.ts を編集
- /api/* を http://localhost:8080/* に転送
Step 6: QueryClientProviderの設定
web/src/frontend.tsx を編集
- QueryClientProviderでAppをラップ
Step 7: コンポーネント実装
1. PostForm.tsx - usePostsCreate()を使用
2. PostItem.tsx - 投稿一覧の各アイテム
3. PostList.tsx - usePostsList()を使用
4. CommentForm.tsx - useCommentsCreate()を使用
5. CommentItem.tsx - 各コメント表示
6. CommentList.tsx - useCommentsList()を使用
7. PostDetail.tsx - usePostsRead()を使用
8. BBS.tsx - メインコンポーネント
Step 8: 統合
web/src/App.tsx を編集
- BBSコンポーネントをメインに配置
Step 9: スタイリング
web/src/index.css に追加
- 既存のカラースキーム(#fbf0df, #1a1a1a)を踏襲
Step 10: package.jsonにスクリプト追加
{
"scripts": {
"generate": "orval"
}
}
画面レイアウト
+------------------------------------------+
| BBS |
+------------------------------------------+
| 投稿一覧 (左) | 投稿詳細 (右) |
| +--------------+ | +--------------+ |
| | [新規投稿] | | | タイトル | |
| +--------------+ | | 内容 | |
| | Post 1 | | | 投稿日時 | |
| | Post 2 ←選択 | | +--------------+ |
| | Post 3 | | | コメント一覧 | |
| +--------------+ | | [コメント追加]| |
+------------------------------------------+
修正対象ファイル
- web/src/index.ts - APIプロキシ追加
- web/src/frontend.tsx - QueryClientProvider追加
- web/src/App.tsx - BBSコンポーネントに置き換え(ロゴ・APITester削除)
- web/src/index.css - BBSスタイル追加(既存の.api-testerスタイル削除)
- web/package.json - scripts追加
- web/.gitignore - generated/を追加
削除対象ファイル
- web/src/APITester.tsx - 不要になるため削除
新規作成ファイル
- web/orval.config.ts - Orval設定
- web/src/api/fetcher.ts - カスタムfetcher
- web/src/generated/ - Orval自動生成(型・hooks)
- web/src/components/BBS.tsx
- web/src/components/PostList.tsx
- web/src/components/PostItem.tsx
- web/src/components/PostForm.tsx
- web/src/components/PostDetail.tsx
- web/src/components/CommentList.tsx
- web/src/components/CommentItem.tsx
- web/src/components/CommentForm.tsx