用程式碼生成簡化 Go 的後端邏輯

用程式碼生成簡化 Go 的後端邏輯

後端開發免不了大量重複的樣板程式碼,尤其圍繞在資料庫查詢和 API 伺服器上。用手寫這些程式碼既慢又容易出錯。程式碼生成工具(codegen)能把宣告式的規格轉成型別安全的 Go 程式碼,你也不用花心力維護。

這篇文章介紹兩個工具。

為什麼要用程式碼生成

用 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
);

用 email 找出使用者的查詢如下。

-- 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.