Money Forward Developers Blog

株式会社マネーフォワード公式開発者向けブログです。技術や開発手法、イベント登壇などを発信します。サービスに関するご質問は、各サービス窓口までご連絡ください。

20230215130734

OpenAPIでスキーマ駆動開発してみた

こんにちは! マネーフォワードでインターンをしている伊藤巧です。

社内向けAPIの開発をスキーマ駆動開発で行いました。 本記事では、それについて紹介します。

「スキーマ駆動開発はなんとなくわかるけど、いまいち進め方のイメージが湧かない。。。」といった方にとって参考になる内容になっています!

※今回はあくまでスキーマ駆動開発の一例を紹介することに留めており、ツールの設定や使用方法等に関する細かい部分は説明していません。

 

技術スタック/ツール

  • OpenAPI Specification 3
  • openapi-generator (コード生成ツール)
  • Rails
  • RSpec
  • Committee
  • ReDoc (ドキュメント生成ツール)
  • Stoplight Studio (OpenAPI用のGUIエディタ)

 

OpenAPI Specificationとは

OpenAPI SpecificationとはRESTful APIの仕様書の記述法で、これに準拠して機械可読な仕様書を作ることで様々なツールを利用して開発の生産性を高めることができます。以降ではOASと呼ぶことにします。

The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. When properly defined, a consumer can understand and interact with the remote service with a minimal amount of implementation logic.

An OpenAPI definition can then be used by documentation generation tools to display the API, code generation tools to generate servers and clients in various programming languages, testing tools, and many other use cases.

公式から引用 https://swagger.io/specification

 

作業手順

最初に今回の内容を相関図に表すと以下の様になります。

主に達成したいことは仕様書を起点にサーバーとクライアントを開発することで両者に乖離がないことを保証することです。 まず仕様書を作成し、仕様書に合わせてサーバーの実装をします。テストにはCommitteeを使用することでサーバーのインタフェースが仕様書通りであることを担保します。 クライアントの開発はopenapi-generatorで仕様書からクライアントコードを自動生成し、それをラップする形で行っています。

作業手順としては以下の様になります。

ここからそれぞれについて詳しく説明していきます。

 

仕様書を作成する

スキーマ駆動開発の要となる仕様書を作成していきます。 OASはyamlとjsonに対応しており、今回はyamlで記述します。 0から仕様書を書くのはハードルが高いという人は仕様書を記述するためのGUIエディタを活用すると始めやすいと思います。 今回はStoplight Studioというツールを使って記述していきます。

こんな感じの画面でポチポチやっていくだけで以下の様なyamlファイルができます。

# openapi.yaml

paths:
  '/users/{user_id}':
    parameters:
      - schema:
          type: integer
        name: user_id
        in: path
        required: true
    get:
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                  age:
                    type: integer
      operationId: get-user

OASではスキーマの制約も設定することができます。(minimum、maximum、minLength... など) スキーマ制約を含めた仕様書の例はopenapi-generatorの章に記載しています。

 

ドキュメント生成ツールを使用する

ドキュメント生成ツールで仕様書からドキュメントを生成し、開発に役立てることができます。 今回はReDocというツールを使用していきます。

仕様書のパスを指定してReDocのDockerコンテナを立てることでドキュメントを生成することができます。

example

docker run --rm -p 8080:80 -e SPEC_URL=/local/openapi.yaml -v "${PWD}:/local" redocly/redoc

公式のexample

docker run -p 8080:80 -e SPEC_URL=https://api.example.com/openapi.yaml redocly/redoc

引用 https://github.com/Redocly/redoc

上記の仕様書からは以下のドキュメントが生成されます。

 

CommitteeでAPIが仕様書通りに実装されていることを検証する

RSpecでテストする場合、committeeというgemを使うとrequest specでサーバーのインタフェースが仕様書に準拠していることを検証することができます。 committeeをrailsに導入するのにcommittee-railsというgemも使用しました。

以下例です。assert_schema_confirm を記述するだけでリクエストとレスポンスが仕様書通りになっているか検証できるので非常に便利です。

# spec/requests/users_controller_spec.rb

RSpec.describe UsersController, type: :request do
  describe "GET /users/:user_id" do
    subject { -> { get "/users/#{user_id}" } }
    
# ~~~~~~~~~~~~~~~~~
    
    it do
      subject.call
      assert_schema_confirm
    end
  end
end

 

openapi-generatorでクライアントコードを自動生成する

クライアントコードとはAPI利用のためのロジックを閉じ込めたクラスで、以下の様にメソッド呼び出しで簡単にAPIの利用をすることができます。

MyAppOpenapiClient::V1Api.new.get_user(user_id)
# => GET /users/user_idが呼ばれる

私たちのチームでは開発工数の削減のためにopenapi-generatorを使用して仕様書からクライアントコードを生成しています。

openapi-generatorのDockerコンテナを立ててgenerateコマンドを実行することでコードの自動生成を行うことができます。読み取る仕様書、生成する言語、出力先などを指定します。

# example
docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli generate \
    -i /local/openapi.yaml
    -g ruby \
    -o /local/out

公式のexmaple

docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli generate \
    -i https://raw.githubusercontent.com/openapitools/openapi-generator/master/modules/openapi-generator/src/test/resources/3_0/petstore.yaml \
    -g go \
    -o /local/out/go

引用 https://github.com/OpenAPITools/openapi-generator

自動生成したコードの一部抜粋

# out/my-app-openapi-ruby-client/api/v1_api.rb

module MyAppOpenapiClient
  class V1Api
    def get_user(user_id, opts = {})
      data, _status_code, _headers = get_user_with_http_info(user_id, opts)
      data
    end
  end
end

また、仕様書でスキーマの制約を設定しておくと引数やレスポンスの検証をしてくれるようになります。実際に制約を設定した上でコードを生成してみます。

# openapi.yaml

paths:
  '/users/{user_id}':
    parameters:
      - schema:
          type: integer
          minimum: 1
        name: user_id
        in: path
        required: true
    get:
      summary: Your GET endpoint
      tags:
        - V1
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    minimum: 1
                  name:
                    type: string
                    maxLength: 30
                  age:
                    type: integer
                    minimum: 0
                required:
                  - id
                  - name

すると、仕様書に設定したとおりに引数とレスポンスの検証が行われていることが確認できます。

# 引数の検証
# out/my-app-openapi-ruby-client/api/v1_api.rb

if @api_client.config.client_side_validation && user_id.nil?
    fail ArgumentError, "Missing the required parameter 'user_id' when calling V1Api.get_user"
  end
if @api_client.config.client_side_validation && user_id < 1
    fail ArgumentError, 'invalid value for "user_id" when calling V1Api.get_user, must be greater than or equal to 1.'
end

# レスポンスの検証
# my-app-openapi-ruby-client/models/inline_response200.rb
def valid?
  return false if @id.nil?
  return false if @id < 1
  return false if @name.nil?
  return false if @name.to_s.length > 30
  return false if !@age.nil? && @age < 0
  true
end

OASのオプションを使いこなせば達成できることも多いですが、それでは補いきれない部分に関してはラッパーを作成することで補いました。

 

まとめ

今回は私たちのチームで行っているスキーマ駆動開発の流れと使用ツールをざっくりと紹介してみました。 少しでも皆さんの参考になることがあれば幸いです。


マネーフォワードでは、エンジニアを募集しています。 ご応募お待ちしています。

【サイトのご案内】 ■マネーフォワード採用サイトWantedly京都開発拠点

【プロダクトのご紹介】 ■お金の見える化サービス 『マネーフォワード ME』 iPhone,iPad Android

ビジネス向けバックオフィス向け業務効率化ソリューション 『マネーフォワード クラウド』

おつり貯金アプリ 『しらたま』

お金の悩みを無料で相談 『マネーフォワード お金の相談』

だれでも貯まって増える お金の体質改善サービス 『マネーフォワード おかねせんせい』

金融商品の比較・申し込みサイト 『Money Forward Mall』

くらしの経済メディア 『MONEY PLUS』