Edited at

OpenAPI generatorを試してみる


OpenAPI

OpenAPI、正確にはOpenAPI SpecificationはREST APIの定義を記述するための規格です。

つまり、APIサーバがどのような挙動をするのかを記した設計書のようなものです。

2015年末まではSwagger Specificationと呼ばれていたので、こちらの方を知っている方もいるかもしれません。

さて、これがあると何が嬉しいかというといろいろ嬉しいことがあるのですが、主なメリットは次の3点です。


  1. APIを決まったフォーマットで規定できる

  2. そのままAPIドキュメントになる

  3. API定義からサーバやクライアントのコード生成ができる

Swagger時代からSwagger Specificationに関連したツールが出回っており、ツールを利用することで手軽に上記のメリットを享受できるようになっています。

メリット
代表的なツール

APIを決まったフォーマットで規定できる
OpenAPI Specification
Swagger Specification

APIドキュメントになる
Swagger UI

コード生成ができる
OpenAPI generator
Swagger Codegen

他にもOpenAPI Specificationを利用した様々なツールが作成されているので、気になる方は公式レポジトリのリストを覗いてみてください。

https://github.com/OAI/OpenAPI-Specification/blob/master/IMPLEMENTATIONS.md

本稿ではSwaggerとOpenAPIやバージョン2と3の話はOpenAPI generatorの話から脱線してしまうため触れないこととし、OpenAPI v3.0を対象とします。


OpenAPI Specification

JSONまたはYAML形式でAPIのrequest endpointやparameter, responseを記述することができます。

例えば、次のようなYAMLファイルを記述することで、APIが /pets というend pointを持つことや、200 OKのレスポンスにidnameを持つリストがjson形式で返ってくることがわかります。


simple-petstore

openapi: "3.0.0"

info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: http://petstore.swagger.io/v1
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string

このYAMLは こちらのsampleを簡略化したもの。


OpenAPI generator

OpenAPI generatorは先程述べたメリットの3番目、OpenAPI Specificationを元にクライアントやサーバのコードを生成するソフトウェアです。

もともとSwaggerの時代からSwagger CodeGenとしてコード生成のツールは提供されており今でも利用可能なのですが、どうも開発者同士で反りが合わなかったらしくコミュニティ主導のツールとしてOpenAPI Specificationは誕生しました。

OpenAPI generatorの開発者がそのあたりの経緯を書いています。(日本語)

https://ackintosh.github.io/blog/2018/05/12/openapi-generator/

私が2018年9月頃にSwagger CodeGenを触った感触でやはりSwagger CodeGenはOpenAPI Specification v3.0への対応が遅れていると感じたため、2019年3月現時点でもOASv3.0を利用する場合は様々な言語に対応しているOpenAPI generatorを利用するのが良さそうです。

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


環境

利用方法はいくつかあるのですが、今回はdocker imageを利用します。

macでDocker Desktop for Macを利用しています。


$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.3
BuildVersion: 18D109
$ docker --version
Docker version 18.09.1, build 4c52b90

生成したコードを出力する場所を作っておきます。

また、先程の簡略化petstore.yamlをカレントディレクトリにおいておきます。

$ mkdir openapi_generator_sample

$ cd openapi_generator_sample/
$ cp /somewhere/petstore.yaml ./


コード生成

こちらにかかれている通り、次のコマンドで Flask/Python のサーバサイドコードが生成されます。


Flaskコード生成

$ GENERATOR=python-flask

$ docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli generate -i /local/petstore.yaml -g ${GENERATOR} -o /local/out/${GENERATOR}

長いですが、dockerでopenapitools/openapi-generator-cliイメージのgenerateコマンドを呼び出して、次のオプションを渡しているだけです。

オプション
意味

-g
生成するコードの種類を指定

-i
OpenAPI Specificationのyamlファイルを指定

-o
出力する場所を指定

上記コードを実行すると out/python-flask フォルダの中にFlaskのコード(ボイラープレート/scaffold)が生成されます。


Flaskアプリ

$ tree out/python-flask/

out/python-flask/
├── Dockerfile
├── README.md
├── git_push.sh
├── openapi_server
│   ├── __init__.py
│   ├── __main__.py
│   ├── controllers
│   │   ├── __init__.py
│   │   ├── pets_controller.py
│   │   └── security_controller_.py
│   ├── encoder.py
│   ├── models
│   │   ├── __init__.py
│   │   ├── base_model_.py
│   │   ├── error.py
│   │   └── pet.py
│   ├── openapi
│   │   └── openapi.yaml
│   ├── test
│   │   ├── __init__.py
│   │   └── test_pets_controller.py
│   └── util.py
├── requirements.txt
├── setup.py
├── test-requirements.txt
└── tox.ini

5 directories, 21 files



対応言語

別の言語でもGENERATOR部分を変えることによって簡単に同等のコードを生成できます。例えばRuby on Railsの場合は次のようにします。


RoRコード生成

$ GENERATOR=ruby-on-rails

$ docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli generate -i /local/petstore.yaml -g ${GENERATOR} -o /local/out/${GENERATOR}


RoRアプリ


$ tree out/ruby-on-rails/
out/ruby-on-rails/
├── Dockerfile
├── Gemfile
├── README.md
├── Rakefile
├── app
│   ├── channels
│   │   └── application_cable
│   │   ├── channel.rb
│   │   └── connection.rb
│   ├── controllers
│   │   ├── application_controller.rb
│   │   └── pets_controller.rb
│   ├── jobs
│   │   └── application_job.rb
│   ├── mailers
│   │   └── application_mailer.rb
│   ├── models
│   │   ├── application_record.rb
│   │   ├── error.rb
│   │   └── pet.rb
│   └── views
│   └── layouts
│   ├── mailer.html.erb
│   └── mailer.text.erb
├── bin
│   ├── bundle
│   ├── rails
│   ├── rake
│   ├── setup
│   └── update
├── config
│   ├── application.rb
│   ├── boot.rb
│   ├── cable.yml
│   ├── database.yml
│   ├── environment.rb
│   ├── environments
│   │   ├── development.rb
│   │   └── production.rb
│   ├── initializers
│   │   ├── active_record_belongs_to_required_by_default.rb
│   │   ├── application_controller_renderer.rb
│   │   ├── backtrace_silencers.rb
│   │   ├── callback_terminator.rb
│   │   ├── cors.rb
│   │   ├── filter_parameter_logging.rb
│   │   ├── inflections.rb
│   │   ├── mime_types.rb
│   │   ├── ssl_options.rb
│   │   └── to_time_preserves_timezone.rb
│   ├── locales
│   │   └── en.yml
│   ├── puma.rb
│   ├── routes.rb
│   ├── secrets.yml
│   └── spring.rb
├── config.ru
├── db
│   ├── migrate
│   │   └── 0_init_tables.rb
│   ├── schema.rb
│   └── seeds.rb
├── docker-entrypoint.sh
├── lib
│   └── tasks
├── log
├── public
│   ├── 404.html
│   ├── 422.html
│   ├── 500.html
│   ├── apple-touch-icon-precomposed.png
│   ├── apple-touch-icon.png
│   ├── favicon.ico
│   └── robots.txt
├── test
│   └── test_helper.rb
├── tmp
│   ├── cache
│   ├── pids
│   ├── restart.txt
│   └── sockets
└── vendor

26 directories, 56 files


GENERATORに入れる値は次のコマンドで調べることができます。

Django等対応していないフレームワークもありますが、結構充実しています。


listコマンド

$ docker run --rm openapitools/openapi-generator-cli list

api-generator-cli list
The following generators are available:

CLIENT generators:
- ada
- android
- apex
- bash
- c
- clojure
- cpp-qt5-client
- cpp-restsdk
- cpp-tizen
- csharp
- csharp-dotnet2
- csharp-netcore
- dart
- dart-jaguar
- eiffel
- elixir
- elm
- erlang-client
- erlang-proper
- flash
- go
- groovy
- haskell-http-client
- java
- javascript
- javascript-closure-angular
- javascript-flowtyped
- jaxrs-cxf-client
- jmeter
- kotlin
- lua
- objc
- perl
- php
- powershell
- python
- r
- ruby
- rust
- scala-akka
- scala-gatling
- scala-httpclient-deprecated
- scalaz
- swift2-deprecated
- swift3-deprecated
- swift4
- typescript-angular
- typescript-angularjs
- typescript-aurelia
- typescript-axios
- typescript-fetch
- typescript-inversify
- typescript-jquery
- typescript-node
- typescript-rxjs

SERVER generators:
- ada-server
- aspnetcore
- cpp-pistache-server
- cpp-qt5-qhttpengine-server
- cpp-restbed-server
- csharp-nancyfx
- erlang-server
- go-gin-server
- go-server
- graphql-nodejs-express-server
- haskell
- java-inflector
- java-msf4j
- java-pkmst
- java-play-framework
- java-undertow-server
- java-vertx
- jaxrs-cxf
- jaxrs-cxf-cdi
- jaxrs-cxf-extended
- jaxrs-jersey
- jaxrs-resteasy
- jaxrs-resteasy-eap
- jaxrs-spec
- kotlin-server
- kotlin-spring
- nodejs-server
- php-laravel
- php-lumen
- php-silex
- php-slim
- php-symfony
- php-ze-ph
- python-aiohttp
- python-blueplanet
- python-flask
- ruby-on-rails
- ruby-sinatra
- rust-server
- scala-finch
- scala-lagom-server
- scalatra
- spring

DOCUMENTATION generators:
- cwiki
- dynamic-html
- html
- html2
- openapi
- openapi-yaml

SCHEMA generators:
- mysql-schema

CONFIG generators:
- apache2
- graphql-schema



サーバを動かしてみる

生成したコードはすぐに起動できるようになっています。

試しに生成したばかりのFlaskを起動してみます。


$ cd out/python-flask/
$ docker build -t openapi_server .
$ docker run -p 8080:8080 openapi_server

これで http://localhost:8080/v1/pets にアクセスして次のようなレスポンスが得られたら成功です。

スクリーンショット 2019-03-25 23.56.01.png


その先

OpenAPI generatorで生成できるサーバサイドのコードはロジックが記述されていません。API定義から生成したので当たり前ですね。そのため、実際にAPIサーバとして利用するためにはこのあとにロジックを書く必要があります。

しかし自動生成したコードにロジックを書くと、API定義に変更があったときにコード側が自動生成で追随できなくなってしまいます。この問題に関する話題はまた別の記事で書きます。


まとめ

今回はOpenAPIの周囲のツールのうち、OpenAPI generatorを触ってみました。

OpenAPI generatorではAPI定義から様々なAPIサーバの雛形を簡単に生成することができました。

API Specificationを先に書くことによって、フロントエンドとサーバサイドの同時開発ができる、コードの雛形の生成ができる、APIドキュメントを別途用意・メンテする必要がなくなるというメリットが享受できます。

個人的にはpetstore.yamlから雛形だけ作成して色々なWebフレームワークの実装の練習に利用しようかなと考えています。


参考

開発者の方の解説。すごくわかりやすかったです。

https://speakerdeck.com/akihito_nakano/gunmaweb34

より詳しく知りたい方には開発者によるeBookが出ていますので、こちらも参考にどうぞ。

REST APIのためのコード生成入門