SODA Engineering Blog
😊

OpenAPI Generator を使った Schema駆動開発

2023/02/27に公開

株式会社SODAの社内勉強会で使用した資料です

こちらは株式会社SODAのエンジニア社内勉強会にて @decoch が発表したときの資料です。
株式会社SODAについては以下リンクなどをご覧ください。

https://recruit.soda-inc.jp/
https://recruit.soda-inc.jp/engineer

これは何?

API開発において、適切なAPI設計が非常に重要です。
この記事では、OpenAPI Generatorを使ったSchema駆動開発について、API定義書からコード生成までの一連の流れを自動化することができる点を説明します。

OpenAPI Generatorとは

OpenAPI Generatorは、APIの設計書として利用される OpenAPI仕様に基づいてコードを自動生成することができるツールのことです。
自動生成されるコードは、多くの言語(Go、Dartなど)に対応し、APIのエンドポイントやリクエスト・レスポンスのデータ構造などが含まれます。

https://www.openapis.org/
https://openapi-generator.tech/

Schema駆動開発とは

Schema駆動開発とは、「APIの構造を中心に開発を進める手法」のことで、
API開発において、APIの仕様書やAPIの実装コードを作成する際には、まずAPIのリクエストとレスポンスのデータ構造(スキーマ)を定義し、後にAPI実装を行うことで、各機能の実装やテストを行いやすくなる開発方法のことです。

OpenAPI Generatorを使ったSchema駆動開発の流れ

OpenAPI Generatorを使ったSchema駆動開発の流れは、以下の通りです。

  1. OpenAPIに基づいてAPI定義を作成
  2. OpenAPI Generatorを使って、API定義からコードを生成
  3. 生成されたコードを元に、実際のAPI実装を行う

API定義を作成する際には、Swagger EditorなどのAPI設計ツールを使用することができます。

順を追って説明すると

1. OpenAPIに基づいてAPI定義を作成

以下のように API 定義を yaml で記述します。

openapi.yaml
openapi: 3.0.3
info:
  description: EC API
  title: EC
  version: 2023.1.1
servers:
- description: Online EC Example Service
  url: http://example.com/v1
tags:
- description: Product Operation
  name: Product
paths:
  /products:
    get:
      operationId: getProducts
      parameters:
      - description: How many products to return at one time (max 100)
        in: query
        name: pageSize
        required: false
        schema:
          format: int32
          maximum: 100
          type: integer
      - description: Page token
        in: query
        name: pageToken
        required: false
        schema:
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/products'
          description: A paged array of products
      summary: Get all products
      tags:
      - Product
  /products/{productId}:
    get:
      operationId: getProductById
      parameters:
      - description: The id of the product
        in: path
        name: productId
        required: true
        schema:
          format: int64
          type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/product'
          description: Expected response to a valid request
      summary: Get a specified product
      tags:
      - Product
components:
  schemas:
    product:
      example:
        localizedName: localizedName
        price: 6
        imageUrl: imageUrl
        name: name
        id: 0
      properties:
        id:
          format: int64
          type: integer
        name:
          type: string
        localizedName:
          type: string
        imageUrl:
          nullable: true
          type: string
        price:
          format: int64
          nullable: true
          type: integer
      required:
      - id
      - imageUrl
      - localizedName
      - name
      type: object
    products:
      allOf:
      - $ref: '#/components/schemas/page'
      - $ref: '#/components/schemas/products_allOf'
    page:
      properties:
        nextPageToken:
          type: string
      type: object
    products_allOf:
      properties:
        items:
          items:
            $ref: '#/components/schemas/product'
          type: array
      type: object
      example: null

2. OpenAPI Generatorを使って、API定義からコードを生成

以下コマンドを実行することでコード生成を行えます。

brew install openapi-generator
openapi-generator generate -i docs/openapi.yaml -g go-gin-server -p packageVersion=0.0.1 -o go-gin

3. 生成されたコードを元に、実際のAPI実装を行う

生成されたコードを変更してAPIの実装を行います。
変更完了したら .openapi-generator-ignore に追加すると自動生成で上書きされなくなります。

api_product.go
/*
 * EC
 *
 * EC API
 *
 * API version: 2023.1.1
 * Generated by: OpenAPI Generator (https://openapi-generator.tech)
 */

package openapi

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// DeleteProductById - Cancel a specified product
func DeleteProductById(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{})
}

// GetProductById - Get a specified product
func GetProductById(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{})
}

// GetProducts - Get all products
func GetProducts(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{})
}

OpenAPI Generatorのメリット

OpenAPI Generatorを使ったSchema駆動開発には、以下のようなメリットがあります。

  1. コードの自動生成による効率化。
    API定義を作成するだけで、APIの実装コードを自動生成することができるため、API実装にかかる時間を短縮することができる
  2. API定義に基づいた実装のため、APIの挙動が仕様に準拠していることが保証される。
    API定義を元に自動生成されたコードは、API定義に準拠するため、APIの挙動と定義がずれない。
  3. ドキュメントの自動生成による開発効率化。
    OpenAPI Generatorを使ったAPI実装には、APIのドキュメントを自動生成する機能があり、APIのドキュメント作成にかかる時間を短縮することができる

Validator/Linter

API定義に関するルールを lint で防ぎたい場合は、ライブラリがあるので導入すると良いと思います。

https://github.com/IBM/openapi-validator/

まとめ

OpenAPI Generatorを使ったSchema駆動開発により、APIの開発・実装が効率化されるだけでなく、API仕様書の作成やドキュメントの自動生成にも役立ちます
API開発において、OpenAPI Generatorを使ってSchema駆動開発を取り入れてみてはいかがでしょうか?

サンプルリポジトリ

https://github.com/decoch/openapi-example-go

SODA Engineering Blog
SODA Engineering Blog

Discussion