公開API開発では、ドキュメントと実装の一貫性を保ちながら効率的に進めることが、利用者の開発体験を向上させる上で非常に重要になります。
「スキーマ駆動開発」は、利用者の開発体験を向上させると同時に、APIの開発スピードや品質を高める有効なアプローチです。
本記事ではRuby on RailsでOpenAPI系のライブラリを用いてスキーマ駆動開発を導入するためのステップを紹介します。
スキーマ駆動開発は、APIの仕様(スキーマ)を中心に据えた開発手法です。OpenAPI仕様(旧Swagger)などを用いてAPIの構造や動作を定義し、その定義を基に開発を進めていきます。この手法では、APIの設計が開発の起点となり、その後のプロセスを効率化・自動化します。
RailsでOpenAPI系のライブラリを駆使してスキーマ駆動開発を行うことで、以下のようなメリットを享受できます。
APIドキュメントの管理コスト低減 RSpecのテストコードを記述するだけで、それに対応したOpenAPIスキーマやAPIドキュメントが自動生成される
リクエストバリデーションの効率化 RSpecやOpenAPIスキーマで定義された仕様に反するリクエストが送られた場合、ミドルウェアでエラーを自動的に返せる ソースコード上では、OpenAPIに記載されないビジネスロジックに関するエラーハンドリングだけを実装すればよくなる
API品質の改善・デグレの防止 OpenAPIスキーマをもとにE2Eテストを生成できるため、APIドキュメントの記載内容と相反するデグレを事前に検知できる
OpenAPIスキーマの作成: スキーマ駆動開発の起点となるOpenAPIスキーマの作成方法を説明します
開発者向けAPIドキュメントの自動生成: 作成したスキーマを基に、どのようにして自動的にAPIドキュメントを生成するかを紹介します
バリデーション・自動テストの自動生成: スキーマを用いて、リクエストやレスポンスの自動検証を行う方法など
ソースコードの自動生成・実装: スキーマとテストに基づいて、実際のコードを実装する手順を説明します。
API仕様のマスタになるスキーマの書き方について、本記事では https://github.com/rswag/rswagを使用してRSpecテストからAPI仕様のスキーマを生成する手法を紹介します。
OpenAPIスキーマを直接yaml/jsonで記述する従来の方法に比べ、テスト駆動開発的なアプローチにより以下の利点があります。
再利用性 同様の構造を持つエンドポイントのスキーマやテストケースを効率的に再利用しやすい
一貫性 実装とテスト、スキーマの一貫性を担保しやすい(ソースコードが意図せずテストやスキーマの内容からずれてしまっても検知できる)
自動テスト環境との統合 テストを実行しているCI/CDに組み込むことで自動テストとAPIドキュメントのデプロイをシームレスに行うことができる
ここからは、具体的にサンプルコードを交えながら実装例を紹介します。
必要なgem
# Gemfile
gem 'rswag-api'
gem 'rswag-ui'
gem 'rswag-specs'
RSpecの例
# spec/requests/api/v1/pets_spec.rb
require 'swagger_helper'
RSpec.describe 'ペットAPI', type: :request do
path '/api/v1/pets' do
post 'ペットを作成する' do
tags 'ペット'
description 'システムに新しいペットを登録します'
consumes 'application/json'
produces 'application/json'
parameter name: :pet, in: :body, schema: {
type: :object,
properties: {
name: {
type: :string,
description: 'ペットの名前'
},
species: {
type: :string,
description: 'ペットの種類(例:犬、猫、鳥)'
},
age: {
type: :integer,
description: 'ペットの年齢(年単位)'
}
},
required: ['name', 'species']
}
response '201', 'ペットが作成されました' do
description '作成されたペットオブジェクトを返します'
schema type: :object,
properties: {
id: { type: :integer, description: 'ペットの一意識別子' },
name: { type: :string, description: 'ペットの名前' },
species: { type: :string, description: 'ペットの種類' },
age: { type: :integer, description: 'ペットの年齢' }
}
let(:pet) { { name: 'ポチ', species: '犬', age: 3 } }
run_test!
end
response '422', '無効なリクエスト' do
description 'ペット作成時のエラーを返します'
let(:pet) { { name: 'ポチ' } }
run_test!
end
end
end
end
このテストは、APIのエンドポイント、リクエストパラメータ、期待されるレスポンスを定義しています。
rswagのDSLではparameterでリクエストパラメータの定義を記述 し、schema レスポンスの形式を記述します。consumes , produces はそれぞれリクエスト・レスポンスのデータ形式(ここでは application/json )です。
テストを書いたら、以下のコマンドでスキーマを生成します。
rake rswag:specs:swaggerize
これにより、swagger/v1/swagger.yamlファイルが生成されます。
このファイルを様々なライブラリに読み込ませることでドキュメントの生成やバリデーション・コードの自動生成が可能になります。
生成されたスキーマを基に、自動的に対話型のAPIドキュメントを生成できます。
# config/initializers/rswag_ui.rb
Rswag::Ui.configure do |c|
c.swagger_endpoint '/api-docs/v1/swagger.yaml', 'API V1 Docs'
end
# config/routes.rb
Rails.application.routes.draw do
mount Rswag::Ui::Engine => '/api-docs'
mount Rswag::Api::Engine => '/api-docs'
endこれにより、/api-docsにアクセスすることで、自動生成されたAPIドキュメントを閲覧できます。
CI/CDにこのタスクを組み込むことで、外部向けに公開しているAPIドキュメントを自動更新することもできるようになります。
生成されたスキーマを使用して、リクエストとレスポンスの自動バリデーションを実装できます。ここではcommittee-railsを使用します。
今回はリクエスト・レスポンスのバリデーションをそれぞれ以下の用途で利用します。
リクエストバリデーション: 本番環境で実際にユーザーから来たリクエストに対して、スキーマに基づいて不正なリクエストにエラーを出す
レスポンスバリデーション: 開発環境やステージング環境などでCI/CDやE2Eテストに組み込み、OpenAPIスキーマの記述に反する意図しないデグレを検知する
スキーマを設定したら、ある程度バリデーションのロジックが自明になるため、エラーの返却や検出を自動化していくのがよいでしょう。
具体的には、committeeというライブラリを用いて下記を実現します。
個別にリクエストバリデーションを実装しなくても、スキーマの定義から外れたリクエストにエラーを返せる
何かしらの原因(デグレ)などにより、レスポンスの形式がスキーマからズレてしまった場合に、検出できるようにする
# Gemfile
gem 'committee-rails'
# config/initializers/committee.rb
Rails.application.config.middleware.use Committee::Middleware::RequestValidation,
schema_path: 'swagger/v1/swagger.yaml',
parse_response_by_content_type: true
# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
include Committee::Rails::Test::Methods
def assert_schema_conform
assert_request_schema_conform
assert_response_schema_conform
end
endcommittee-railsは、assert_request_schema_conformとassert_response_schema_conformという2つの主要なメソッドを提供します。
assert_request_schema_conform リクエストバリデーション 受信したリクエストがOpenAPI仕様に適合しているかを検証します。 使用例: 外部APIを管理するControllerの先頭においておくのがよいでしょう
assert_response_schema_conform レスポンスバリデーション 送信するレスポンスがOpenAPI仕様に適合しているかを検証します。 使用例: 本番環境でなく、E2Eテストでのみ呼び出される形にしておくのがよいでしょう
これにより、全てのリクエストとレスポンスがOpenAPIスキーマに基づいて自動的に検証されるようになります。
ドキュメントやバリデーションなどインターフェイス寄りの自動生成が済んだら、あとはロジック部分を実装していくのみになります。
の自動生成が済んだら、あとはロジック部分を実装していくのみになります。
OpenAPI Generatorなどでコードを自動生成する手段もありますが、バグが多く、手作業との差が小さいと感じるシーンも多くあります。最近では生成AIを用いてスキーマを基にコードを生成する方法がより効率的です。
OpenAPIスキーマを利用したコードやクライアントSDKの自動生成については生成AIを用いたライブラリやBaaS(Backend as a Service)も近年増えており、別記事で詳細に取り扱わせて頂ければと思います。
committeeで返却する400 errorのメッセージはデフォルトだと英語で、かつ抽象的な文言になってしまいます。
例)
{
"status": 400,
"error": "invalid_request",
"message": "Invalid request parameters",
"details": [
{
"location": "body",
"parameter": "name",
"message": "is required"
}
]
}エラーメッセージを日本語に変更したり、より具体的に記述するにはエラーハンドラのカスタマイズする等の対処法を検討する必要があります。
この点がcommitteeを用いて国内でAPI公開する場合にネックになる部分ですが、最初に基盤・ルールだけ整えつつカスタマイズすれば、それ以降のAPIメンテナンスコストは大きく下がるでしょう。
スキーマ駆動開発は導入時に準備が必要ですが、一度整備すればその後の開発とメンテナンスが大幅に効率化されます。
特に動的型付け言語のRubyでの開発で、データ型などをスキーマに基づいて自動チェックして品質向上させられるのは大きなメリットと言えるのではないでしょうか。
本メディアはAPI特化の開発会社であるECU株式会社が運営しています。記事についてのご質問やAPIに関するご相談・お問い合わせはお気軽にお問い合わせフォームまで。