はじめに
最近、Claude CodeとSupabase MCP Serverを使って画像管理APIを作ってみました。
この記事では、実際に開発した時の体験や、うまくいったこと・つまづいたことを書いてみます。同じような開発をする人の参考になればと思います。
プロジェクト概要
技術スタック
- 言語: Go 1.22
- フレームワーク: Gin
- データベース: Supabase PostgreSQL
- ストレージ: Supabase Storage(S3互換)
- アーキテクチャ: クリーンアーキテクチャ
- 開発環境: Docker Compose
- CI/CD: GitHub Actions
プロジェクト構造
image-api/
├── backend/ # Go APIサーバー
│ ├── pkg/
│ │ ├── domain/ # ドメイン層
│ │ ├── usecase/ # ユースケース層
│ │ ├── interface/ # インターフェース層
│ │ └── infrastructure/ # インフラストラクチャ層
│ └── cmd/api/main.go # エントリポイント
├── .github/workflows/ # CI/CD設定
└── CLAUDE.md # Claude用技術仕様
Supabase MCP Serverとの連携
MCP Serverの初期設定
Claude CodeでSupabase MCP Serverを使用するには、まず以下のコマンドでセットアップを行います:
claude mcp add -s local supabase npx -- -y @supabase/mcp-server-supabase@latest --access-token <your-personal-access-token>
このコマンドにより、Claude CodeからSupabaseの各種操作を直接実行できるようになります。Personal Access Tokenは、Supabaseダッシュボードの「Access Tokens」セクションから生成できます。
利用可能なMCP操作
セットアップ完了後、以下のような操作をClaude Code内で直接実行できます:
# プロジェクト一覧取得
mcp__supabase__list_projects
# 組織情報確認
mcp__supabase__list_organizations
# テーブル一覧確認
mcp__supabase__list_tables
# SQL実行
mcp__supabase__execute_sql
# マイグレーション適用
mcp__supabase__apply_migration
# Edge Functions管理
mcp__supabase__list_edge_functions
mcp__supabase__deploy_edge_function
# TypeScript型生成
mcp__supabase__generate_typescript_types
実際に使ってみて感じたメリット
MCP Serverを入れてから、開発の流れがかなり変わりました。今まではSupabaseのダッシュボードとローカル環境を行ったり来たりしていましたが、それが全部Claude Code内で完結するようになったんです。
特に便利だと感じたのは:
- テーブルいじりが楽: スキーマ変更もその場でできちゃう
- データ確認が早い: 「今どんなデータ入ってるっけ?」がすぐ分かる
- 型定義の更新忘れがない: TypeScript使ってる人には助かる機能
- エラー調査が楽: ログもその場で見れる
実際の開発フロー
-
プロジェクト選択:
mcp__supabase__list_projectsでプロジェクト確認 -
スキーマ設計:
mcp__supabase__apply_migrationでテーブル作成 -
データ投入:
mcp__supabase__execute_sqlでテストデータ作成 - Go実装: Claude Codeでアプリケーション実装
-
動作確認:
mcp__supabase__get_logsでエラー確認 -
型定義更新:
mcp__supabase__generate_typescript_typesで最新型取得
データベース実装
接続方式の選択
Supabaseって接続方法がいくつかあるんですが、今回はPooler接続を使いました。最初はDirect接続を試したんですが、なんかIPv6周りでエラーが出て...。Pooler接続にしたら安定して動くようになりました。
# Pooler接続設定
SUPABASE_DB_HOST=aws-0-ap-northeast-1.pooler.supabase.com
SUPABASE_DB_USER=postgres.project_ref
SUPABASE_DB_PORT=5432
環境変数の体系的管理
プロジェクトでは環境変数を体系的に管理するため、カテゴリ別に整理しています:
# .env.example
# Supabase API設定
SUPABASE_URL=https://your-project-ref.supabase.co
SUPABASE_ANON_KEY=your_anon_key_here
SUPABASE_SERVICE_KEY=your_service_role_key_here
# データベース接続(Pooler経由)
SUPABASE_DB_HOST=aws-0-region.pooler.supabase.com
SUPABASE_DB_PORT=5432
SUPABASE_DB_USER=postgres.your_project_ref
SUPABASE_DB_PASSWORD=your_database_password
SUPABASE_DB_NAME=postgres
# ストレージ設定(S3互換)
S3_ACCESS_KEY_ID=your_s3_access_key_id
S3_SECRET_ACCESS_KEY=your_s3_secret_access_key
S3_ENDPOINT=https://your-project-ref.supabase.co/storage/v1/s3
S3_BUCKET=images
S3_REGION=us-east-1
# サーバー設定
SERVER_PORT=8080
この設定により、開発・ステージング・本番環境での環境変数の切り替えが容易になります。
スキーマ設計とマイグレーション
MCP Serverを使用してテーブル設計を行います。以下は画像管理のための基本的なスキーマです:
-- Claude Code内でmcp__supabase__apply_migrationを使用して実行
CREATE TABLE images (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
image_url TEXT,
file_size INTEGER,
content_type VARCHAR(100),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- インデックス追加
CREATE INDEX idx_images_created_at ON images(created_at);
CREATE INDEX idx_images_name ON images(name);
ストレージ実装
ファイルアップロード実装
func (s *S3Storage) UploadFile(file multipart.File, header *multipart.FileHeader) (string, error) {
// タイムスタンプベースのユニークファイル名生成
timestamp := time.Now().Unix()
ext := filepath.Ext(header.Filename)
filename := fmt.Sprintf("%d%s", timestamp, ext)
// Content-Type自動判定
contentType := "application/octet-stream"
switch ext {
case ".jpg", ".jpeg":
contentType = "image/jpeg"
case ".png":
contentType = "image/png"
case ".gif":
contentType = "image/gif"
case ".webp":
contentType = "image/webp"
}
// S3アップロード
_, err := s.client.PutObject(&s3.PutObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(filename),
Body: file,
ContentType: aws.String(contentType),
ACL: aws.String("public-read"),
})
if err != nil {
return "", fmt.Errorf("failed to upload file to S3: %w", err)
}
// 公開URLの生成
publicURL := fmt.Sprintf("https://project-ref.supabase.co/storage/v1/object/public/%s/%s", s.bucket, filename)
return publicURL, nil
}
バリデーション機能
func (h *ImageHandler) UploadImage(c *gin.Context) {
// ファイルサイズ制限(5MB)
err := c.Request.ParseMultipartForm(5 << 20)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "File too large (max 5MB)"})
return
}
// ファイル形式チェック
ext := strings.ToLower(filepath.Ext(header.Filename))
allowedExts := []string{".jpg", ".jpeg", ".png", ".gif", ".webp"}
isValidExt := false
for _, allowedExt := range allowedExts {
if ext == allowedExt {
isValidExt = true
break
}
}
if !isValidExt {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file format"})
return
}
}
API設計とSwagger統合
エンドポイント設計
// 画像アップロード専用エンドポイント
// @Summary Upload image
// @Tags images
// @Accept multipart/form-data
// @Param image formData file true "Image file"
// @Success 200 {object} map[string]string
// @Router /images/upload [post]
func (h *ImageHandler) UploadImage(c *gin.Context)
// 画像作成(メタデータ付き)
// @Summary Create image with metadata
// @Tags images
// @Accept multipart/form-data
// @Param name formData string true "Image name"
// @Param description formData string true "Image description"
// @Param image formData file false "Image file"
// @Success 201 {object} entity.Image
// @Router /images/with-metadata [post]
func (h *ImageHandler) CreateWithImage(c *gin.Context)
Swagger UIの活用
開発中はSwagger UIを使用してAPI動作確認を行いました:
- URL: http://localhost:8081
- ファイルアップロードのテスト
- レスポンス形式の確認
CI/CD パイプラインの構築
GitHub Actionsワークフロー
name: Backend CI
on:
push:
branches: [ main, develop ]
paths:
- 'backend/**'
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:17
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: image_api_test
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.23'
- name: Run tests
env:
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
# その他の環境変数...
run: go test ./... -v
- name: Build application
run: go build -v ./cmd/api
セキュリティ考慮事項
-
環境変数の適切な管理
-
.envファイルを.gitignoreに追加 - GitHub Secretsでの機密情報管理
-
-
ファイルアップロードのセキュリティ
- ファイルサイズ制限
- 許可ファイル形式の制限
- ユニークファイル名の生成
Claude Codeの活用ポイント
1. リアルタイムなデバッグ支援
# データベース接続確認
make db-exec SQL="SELECT * FROM images;"
# ログの確認
make logs
# 環境変数の確認
echo $SUPABASE_DB_HOST
2. 段階的な実装
Claude Codeのおかげで以下の順序で段階的に実装できました:
- 基本的なCRUD API実装
- データベース接続とテストデータ投入
- ストレージ統合(複数の認証方式を試行)
- エラーハンドリングとバリデーション強化
- CI/CD パイプライン構築
3. 技術文書の自動生成
CLAUDE.mdファイルを作成し、次回のセッションで即座にプロジェクト理解ができるようにしました:
# 画像管理API - Claude用技術仕様
## 新機能実装手順
1. `pkg/domain/entity/` にエンティティ定義
2. `pkg/domain/repository/` にリポジトリIF定義
3. `pkg/usecase/input,output/` にDTO定義
4. `pkg/usecase/interactor/` にビジネスロジック
5. `pkg/interface/handler/` にHTTPハンドラー
6. `pkg/infrastructure/persistence/` にDB実装
7. `cmd/api/main.go` でDI登録
まとめ
Supabase MCP ServerとClaude Codeを活用することで、以下のメリットを得られました:
- 開発効率の向上: リアルタイムなデータベース操作とデバッグ
- 試行錯誤の支援: 複数の実装アプローチを効率的に試行
- 品質の向上: 段階的な実装とテスト駆動開発
- 知識の蓄積: 技術仕様書の自動生成と保守
特にストレージとデータベース周りでは、認証方式の選択や接続設定で多くの試行錯誤が必要でしたが、Claude Codeのサポートにより効率的に解決できました。
この開発経験は、モダンなAPI開発におけるAI支援開発の可能性を示す良い経験となったと考えています。