Goでコード生成を使ってバックエンドのロジックをシンプルにする

Goでコード生成を使ってバックエンドのロジックをシンプルにする

バックエンド開発には決まりきったコードがつきものだ。特にデータベースのクエリやAPIサーバーの周りで顕著になる。そうしたコードを手で書くのは時間がかかるし、間違いも起きやすい。コード生成ツール(codegen)なら、宣言的な仕様から型安全なGoコードを自動で作ってくれる。メンテナンスの手間も省ける。

この記事では2つのツールを紹介する。

なぜコード生成を使うのか

sqlc でSQLコードを生成する

sqlc とは

sqlc は .sql ファイルを読み込み、型付きのパラメータと結果でクエリを実行するGo関数を生成する。

sqlc の使い方

  1. スキーマとクエリを書く。
  2. sqlc.yaml で入力と出力を設定する。
  3. sqlc generate を実行する。

例

users テーブルがあるとしよう。

-- schema.sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL
);

メールアドレスでユーザーを引くクエリは次の通り。

-- queries.sql
-- name: GetUserByEmail :one
SELECT id, name, email FROM users WHERE email = $1;

sqlc.yaml の設定。

version: "1"
packages:
  - name: "db"
    path: "./db"
    queries: "./queries.sql"
    schema: "./schema.sql"
    engine: "postgresql"

実行する。

sqlc generate

するとこういうメソッドが生成される。

func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error)

バックエンドではこう使う。

user, err := dbQueries.GetUserByEmail(ctx, "alice@example.com")
if err != nil {
    // handle error
}
fmt.Println("User:", user.Name)

SQL文字列を手で組み立てたり行を走査したりする作業が消える。コードはよりきれいで安全になる。

oapi-codegen でサーバーコードを生成する

oapi-codegen とは

oapi-codegen は OpenAPI の仕様から Go のサーバーコードとクライアントコードを生成する。インターフェースとリクエスト・レスポンスの型を作ってくれる。残るのはビジネスロジックの実装だけ。

oapi-codegen の使い方

  1. OpenAPI の YAML か JSON で API を定義する。
  2. oapi-codegen を実行する。
  3. 生成されたインターフェースのメソッドを実装する。

例

小さな OpenAPI の仕様 api.yaml を用意する。

openapi: 3.0.0
info:
  title: User API
  version: 1.0.0
paths:
  /users/{email}:
    get:
      summary: Get user by email
      parameters:
        - name: email
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: User found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "404":
          description: User not found
components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        email:
          type: string

サーバーコードを生成する。

oapi-codegen -generate types,server -package api -o api.gen.go api.yaml

これでリクエスト・レスポンスの型(User struct)と、メソッドを持つ ServerInterface が生成される。

GetUsersEmail(ctx context.Context, email string) (api.User, error)

インターフェースを実装する。

type ServerImpl struct {
    db *db.Queries
}
 
func (s *ServerImpl) GetUsersEmail(ctx context.Context, email string) (api.User, error) {
    user, err := s.db.GetUserByEmail(ctx, email)
    if err != nil {
        return api.User{}, err
    }
    return api.User{
        Id:    int64(user.ID),
        Name:  user.Name,
        Email: user.Email,
    }, nil
}

サーバーをつなぐ。

router := api.NewRouter(&ServerImpl{db: dbQueries})
http.ListenAndServe(":8080", router)

sqlc と oapi-codegen を組み合わせる利点

まとめ

sqlc と oapi-codegen を一緒に使えば、Go のバックエンドでデータベースアクセスと API サーバーのコードを自動化できる。書くボイラープレートが減り、実行時のエラーも減る。集中すべきはアプリケーションの中心となるロジックだ。

© Melvin Laplanche - All rights reserved.