API仕様書をOpenAPIで作成、クライアント・スタブを自動生成する

# TL;DR
今更ながらOpenAPIGeneratorの存在を知り、世の中にはこんな便利なものがあるのか！！！
と感動したので需要のあるなしにかかわらずOpenAPI周りをまとめて記事にしました。

- OpenAPIとは
- 導入のメリット・デメリット
- ファイルの記述について
- クライアント・スタブの自動生成
- APIモックサーバーの作成
- OpenAPIのPDF・HTML出力 ※こんな方法あるんだなーとリンク貼る程度

について、この記事では説明します

# OpenAPIとは
> OpenAPI Specification (formerly Swagger Specification) is an API description format for REST APIs. An OpenAPI file allows you to describe your entire API, including:

> - Available endpoints (/users) and operations on each endpoint (GET /users, POST /users)
- Operation parameters Input and output for each operation
- Authentication methods
- Contact information, license, terms of use and other information.

> API specifications can be written in YAML or JSON. The format is easy to learn and readable to both humans and machines. The complete OpenAPI Specification can be found on GitHub: OpenAPI 3.0 Specification

出典：https://swagger.io/docs/specification/about/

（超意訳）
OpenAPIはREST APIのためのAPI記述のフォーマットです。
OpenAPIファイルは例えば

- HTTPメソッドを設定したエンドポイント
- インプット・アプトプットパラメータ
- 認証メソッド
- お問い合わせ情報、ライセンス、利用規約などの情報

などのすべてのAPIの情報を記述できます。

APIの仕様はYAMLまたはJSONで記述できます。 
このフォーマットは習得が容易かつ、人間でも機械でも読めます。
 完全なOpenAPI仕様は、GitHub：OpenAPI3.0仕様に存在します。

#導入のメリットとデメリット（超主観）
## メリット
- 記述が平易である
    - yaml形式またはjson形式
- UIが自動で作成される
    - 専用のエディタ（無料）やVSCode拡張が提供されている
    - Wordみたくデザイン崩れた！がない
- FE側のクライアントやBE側のスタブを自動生成できる
- Gitでのバージョン管理ができる
- Gitを使用することで、複数名でのリアルタイムのファイル共有・編集もできる
    - 言わずもがな競合する箇所があるならばGitが教えてくれるし、<br>
    違う箇所を編集しているのならば自動でマージしてくれる

## デメリット
- Gitが使えない会社やVSCode拡張を入れるのに申請がいる会社だと手間がかかる
- 先方にWordファイルなどで納品する必要がある場合、変換がちょっと手間かも

# ファイルの記述について
## エディタについて
ファイルについてはyamlもしくはjson形式で記述できます。
また、SwaggerUIという専用エディタもあり、プレビューしながら編集することが可能です。
VSCodeにも[OpenAPI (Swagger) Editor](https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi)や[Swagger Viewer](https://marketplace.visualstudio.com/items?itemName=Arjun.swagger-viewer)といった拡張機能があり、
これらを使うことで専用エディタと遜色ない機能を利用することが可能です。

![スクリーンショット 2021-08-08 3.20.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288273/07cd2ce1-6a6f-9647-dc34-9d26ec584b73.png)

## 記述方法
説明描こうかなと思ったけど多分なくても伝わるなって思ったからサンプル貼り付けただけ。

```yaml
openapi: 3.0.0
info:
  description: This is a simple API
  version: "1.0.0"
  title: Simple Inventory API
  contact:
    email: you@your-company.com
  license:
    name: Apache 2.0
    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
tags:
  - name: admins
    description: Secured Admin-only calls
  - name: developers
    description: Operations available to regular developers
paths:
  /inventory:
    get:
      tags:
        - developers
      summary: searches inventory
      operationId: searchInventory
      description: |
        By passing in the appropriate options, you can search for
        available inventory in the system
      parameters:
        - in: query
          name: searchString
          description: pass an optional search string for looking up inventory
          required: false
          schema:
            type: string
        - in: query
          name: skip
          description: number of records to skip for pagination
          schema:
            type: integer
            format: int32
            minimum: 0
        - in: query
          name: limit
          description: maximum number of records to return
          schema:
            type: integer
            format: int32
            minimum: 0
            maximum: 50
      responses:
        '200':
          description: search results matching criteria
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/InventoryItem'
        '400':
          description: bad input parameter
    post:
      tags:
        - admins
      summary: adds an inventory item
      operationId: addInventory
      description: Adds an item to the system
      responses:
        '201':
          description: item created
        '400':
          description: 'invalid input, object invalid'
        '409':
          description: an existing item already exists
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InventoryItem'
        description: Inventory item to add
components:
  schemas:
    InventoryItem:
      type: object
      required:
        - id
        - name
        - manufacturer
        - releaseDate
      properties:
        id:
          type: string
          format: uuid
          example: d290f1ee-6c54-4b01-90e6-d701748f0851
        name:
          type: string
          example: Widget Adapter
        releaseDate:
          type: string
          format: date-time
          example: '2016-08-29T09:12:33.001Z'
        manufacturer:
          $ref: '#/components/schemas/Manufacturer'
    Manufacturer:
      required:
        - name
      properties:
        name:
          type: string
          example: ACME Corporation
        homePage:
          type: string
          format: url
          example: 'https://www.acme-corp.com'
        phone:
          type: string
          example: 408-867-5309
      type: object
```

###ドキュメント
https://swagger.io/specification/

# クライアント・スタブの自動生成
[OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator)
というツールを利用して、クライアント・スタブの自動生成ができます。

対応言語は幅広く、
クライアントは例えばTypescriptならば
`AngularJS, Angular (2.x - 11.x), Aurelia, Axios, Fetch, Inversify, jQuery, Nestjs, Node, redux-query, Rxjs`
に対応しており、
スタブについては例えばPHPならば
`Laravel, Lumen, Slim, Silex, Symfony, Zend Expressive`
に対応しています。
全ての対応形式は[こちら](https://github.com/OpenAPITools/openapi-generator/tree/master/docs/generators)。

特にクライアントの生成については、
Typescriptのインターフェースの型定義をすべて自動でしてくれる
（ただし一部誤変換あったりする）ので、
API仕様書が更新されてもその度に
 `generate -i 変換ファイル.yaml -g typescript-axios -o 変換先`
コマンドをdockerで実行するだけなので超絶簡単でした。。。
~~わたしこれ知らなかったらKA☆RO☆SHIしてた。~~

ツールはJavaの実行環境に依存しているので、
Javaをローカルインストールされているか、dockerなどの仮想環境を利用する必要があります。

dockerhubに[openapitools/openapi-generator-cli 公式イメージ](https://hub.docker.com/r/openapitools/openapi-generator-cli)が提供されているため、

```yml:docker-compose
version: '3'
services:
  openapi-generator: 
    image: openapitools/openapi-generator-cli:latest-release
    volumes:
      - ".:/local"
    command: generate -i /local/ファイル -g 変換形式 -o /local/出力先
```

のようなdocker-composeファイルを作って`docker compose up`をすれば
それだけでクライアント・スタブを自動生成することができます。

# APIモックサーバーの作成
[Prism](https://meta.stoplight.io/docs/prism/ZG9jOjYx-overview)を使って、APIのモックサーバーを作成することもできます。

nodeサーバーで動くため

```console
$ npm install -g @stoplight/prism-cli
$ prism mock 
````

のように構築します。

また、こちらもdockerhubに[イメージファイル](https://hub.docker.com/r/stoplight/prism)が用意されており、

```yml:docker-compose
version: '3'
  mock:
    image: stoplight/prism:3
    ports:
      - "4010:4010"
    command: mock -h 0.0.0.0 /hoge/ファイル名
    volumes:
      - ./hoge:/hoge
```

のようなdocker-composeファイルを作ればdockerインストールできるサーバー
（適当なそこらへんのLinuxベースのVPSとか）であればデプロイできます。

API仕様書が更新された場合はyamlファイルをそのままサーバーにポン、と上書きするだけで更新完了です。

# OpenAPIをPDFもしくはHTMLで出力する
納品物としてAPI仕様書が必要、かつ、Wordなどの先方が読める形式にして
納品しなければならないこともあると思います。

そのような場合には、OpenAPIファイルをPDFやHTMLに変換することができます。

https://qiita.com/godgarden/items/be420a8c165f4a0f3ad8

https://dev.classmethod.jp/articles/convert-swagger-including-japanese-to-pdf/

PDFやHTMLに変換さえできれば、Word形式に変更して納品することも可能です。

# いかがでしたでしょうか
今更ながら今回仕事で本当にOpenAPIGeneratorとPrismに助けられたのでまとめ記事にしてみました。
少しでもお役に立てれば幸いです。