AgentSkillsCN

sqlc-goose-integration

运用 sqlc 实现类型安全的 SQL 查询生成,并结合 goose 进行迁移管理的最佳实践。提供配置、查询编写规范以及类型映射等关键内容。

SKILL.md
--- frontmatter
name: sqlc-goose-integration
description: sqlcによる型安全なSQLクエリ生成と、gooseによるマイグレーション管理のベストプラクティス。設定、クエリ記述規約、型マッピングを提供します。

sqlc + goose 統合スキル

概要

sqlcによる型安全なSQLクエリ生成と、gooseによるマイグレーション管理のベストプラクティス。


sqlc 設定

backend/sqlc.yaml に配置:

yaml
version: "2"
sql:
  - engine: "postgresql"
    queries: "sql/query"
    schema: "sql/schema"
    gen:
      go:
        package: "sqlcgen"
        out: "internal/adapter/repository/sqlcgen"
        sql_package: "pgx/v5"
        emit_json_tags: true
        emit_empty_slices: true

クエリファイル記述規約

backend/sql/query/ に配置:

sql
-- name: FindDraftPosts :many
SELECT * FROM posts
WHERE status = 'draft'
ORDER BY created_at DESC;

-- name: FindPostByID :one
SELECT * FROM posts
WHERE id = $1;

-- name: CreatePost :one
INSERT INTO posts (id, status, language_code, scheduled_at, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *;

-- name: AcquirePostLock :execrows
UPDATE posts
SET job_locked_at = NOW(), updated_at = NOW()
WHERE id = $1 AND job_locked_at IS NULL;

goose マイグレーション

backend/sql/schema/ に配置:

sql
-- +goose Up
CREATE TABLE posts (
    id UUID PRIMARY KEY,
    status TEXT NOT NULL DEFAULT 'draft',
    language_code TEXT NOT NULL,
    scheduled_at TIMESTAMP NOT NULL,
    job_locked_at TIMESTAMP,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);

-- +goose Down
DROP TABLE posts;

マイグレーション実行

bash
goose -dir sql/schema postgres "$DATABASE_URL" up

PostgreSQL 型マッピング

PostgreSQLGo
UUIDpgtype.UUID
TEXTstring
TIMESTAMPpgtype.Timestamp
TIMESTAMP (nullable)pgtype.Timestamp
BOOLEANbool
TEXT[][]string

生成コードの利用パターン

go
// Adapter で sqlc 生成コードを利用
func (r *postRepository) FindDraftPosts(ctx context.Context) ([]*entity.Post, error) {
    rows, err := r.queries.FindDraftPosts(ctx)
    if err != nil {
        return nil, fmt.Errorf("find draft posts: %w", err)
    }
    return toPostEntities(rows), nil
}
  • 生成コード(sqlcgen)を直接 UseCase に渡さない
  • Adapter 層で Entity への変換を行う