OpenAPI (Swagger) 超入門

# はじめに

業務でOpenAPIを用いたAPI設計を行っているため、備忘録も兼ねてまとめる。
入門用なので、基本的な部分のみ解説。

# 概要

## OpenAPI

[What Is OpenAPI?](https://swagger.io/docs/specification/about/)
OpenAPIとは、RESTful APIを記述するためのフォーマットのこと。
Swagger 2.0を拡張して実装されている。

## Swagger

Swaggerとは、OpenAPIを用いてREST APIを設計する際に使用するツールセットのこと。

メジャーなものとしては以下。

|ツール名|概要|
|:--|:--|
|[Swagger Editor](http://editor.swagger.io/)|ブラウザベースでOpenAPIを記述できるエディター。[Dockerイメージ](https://hub.docker.com/r/swaggerapi/swagger-editor/)も配布されており、ローカルでの実行も可能。|
|[Swagger UI](https://swagger.io/tools/swagger-ui/)|OpenAPIの記述を、動的にAPIドキュメントとしてレンダリングするツール。上記のSwagger Editorの右半分に出ているのはコレ。|
|[Swagger Codegen](https://github.com/swagger-api/swagger-codegen)|OpenAPIの記述から、サーバー/クライアントのコードを生成するツール。多くの言語に対応。|

# 記述形式

OpenAPIの記述は以下の2種類から選択可能。
どちらの形式で記述しても、アウトプットは同じ。
また`YAML`→`JSON`、`JSON`→`YAML`の相互変換が可能。(Swagger Editorの機能)

* YAML形式
* JSON形式

# 基本構造

本記事では、`YAML`形式で記述していく。

```yaml:sample.yaml
openapi: 3.0.0
info:
  ...
servers:
  ...
paths:
  ...
components:
  ...
security:
  ...
tags:
  ...
externalDocs:
  ...
```

|フィールド名|必須|概要|
|:--|:--|:--|
|openapi|**YES**|セマンティックなバージョニングを記述する。今回は`3.0.0`を用いる。詳しくは[ドキュメント](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#versions)を参照。|
|info|**YES**|APIのメタデータを記述する。|
|servers||APIを提供するサーバーを記述する。配列で複数記述可能(STG, PROD等)。|
|paths|**YES**|APIで利用可能なエンドポイントやメソッドを記述する。|
|components|**YES**|APIで使用するオブジェクトスキーマを記述する。|
|security||API全体を通して使用可能なセキュリティ仕様を記述する。(OAuth等)|
|tags||APIで使用されるタグのリスト。各種ツールによってパースされる際は、記述された順序で出力される。タグ名はユニークで無ければならない。|
|externalDocs||外部ドキュメントを記述する(API仕様書等)。|

# 各フィールドの詳細

## `info`

ドキュメント: [Info Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#info-object)

```yaml:example.yaml
info:
  title: Sample API
  description: A short description of API.
  termsOfService: http://example.com/terms/
  contact:
    name: API support
    url: http://www.example.com/support
    email: support@example.com
  license:
    name: Apache 2.0
    url: http://www.apache.org/licenses/LICENSE-2.0.html
  version: 1.0.0
```

|フィールド名|必須|概要|
|:--|:--|:--|
|title|**YES**|APIの名称。|
|description||APIの簡潔な説明。[CommonMark](https://spec.commonmark.org/)シンタックスが使える。|
|termsOfService||APIの利用規約。URL形式でなければならない。|
|contact||コンタクト情報。(サポートページのURLやメールアドレス等)|
|license||ライセンス情報。ライセンスページのURLも記述可能。|
|version|**YES**|APIドキュメントのバージョン。|

Swagger UIでの表示
<img width="835" alt="スクリーンショット 2018-09-08 13.13.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/241738/b7a8ef24-837f-91bb-9f9b-ccde34191b3b.png">

## `servers`

ドキュメント: [Server Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#server-object)

```yaml:example.yaml
servers:
  - url: https://dev.sample-server.com/v1
    description: Development server
  - url: https://stg.sample-server.com/v1
    description: Staging server
  - url: https://api.sample-server.com/v1
    description: Production server
```

|フィールド名|必須|概要|
|:--|:--|:--|
|url|**YES**|APIサーバーのURL。|
|description||APIサーバーの説明。|

Swagger UIでの表示
複数の場合、ドロップダウンで表示される。

<img width="839" alt="スクリーンショット 2018-09-08 13.24.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/241738/f83858c8-bef1-7728-306d-b6e8112f9395.png">

## `paths`

ドキュメント: [Paths Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#pathsObject)

```yaml:example.yaml
paths:
  # paths オブジェクト
  /users:
    # path item オブジェクト
    get: # GET
      # Operationオブジェクト
      tags:
        - users
      summary: Get all users.
      description: Returns an array of User model
      parameters: []
      responses: # レスポンス定義
        '200': # HTTP status
          description: A JSON array of User model
          content:
            application/json: # レスポンスの形式指定
              schema: 
                type: array
                items:
                  $ref: '#/components/schemas/User' # 参照するモデル
                example: # サンプルデータ
                  - id: 1
                    name: John Doe
                  - id: 2
                    name: Jane Doe
    post: # POST
      tags: 
        - users
      summary: Create a new User
      description: Create a new User
      parameters: []
      requestBody: # リクエストボディ
        description: user to create
        content:
          application/json:
            schema: # POSTするオブジェクト
              $ref: '#/components/schemas/User'
            example:
              id: 3
              name: Richard Roe
      responses:
        '201':
          description: CREATED
  /users/{userId}:
    get:
      tags:
        - users
      summary: Get user by ID.
      description: Returns a single User model
      parameters: # リクエストパラメータ
        - name: userId
          in: path # パラメータをパス内に含める
          description: user id
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: A single User model
          content:
            application/json:
              schema: 
                type: object
                items:
                  $ref: '#/components/schemas/User'
                example:
                  id: 1
                  name: John Doe
```

### `paths`オブジェクト

|フィールド名|概要|
|:--|:--|:--|
|/{path}|各エンドポイントのパスを記述する。`servers`で定義したURLにこのパスを結合したものが最終的なエンドポイントとなる。|

### `path item`オブジェクト

|フィールド名|概要|
|:--|:--|:--|
|summary|エンドポイントのサマリ。|
|descriptions|エンドポイントの簡潔な説明。CommonMarkシンタックスを使用可能。|
|get, post, delete...|HTTPメソッドを定義。GET, PUT, POST, DELETE, OPTIONS, DELETE, PATCH, TRACEが使用可能。|

Swagger UIでの表示

<img width="821" alt="スクリーンショット 2018-09-08 15.00.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/241738/569584f2-5640-5762-8698-9cc58a03f6d9.png">

<img width="815" alt="スクリーンショット 2018-09-08 15.19.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/241738/041b9fe2-25f7-03b4-1ed3-e09264844cd8.png">

<img width="814" alt="スクリーンショット 2018-09-08 15.27.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/241738/74d52265-a051-fcf1-418a-7fe2a9a00768.png">



## `components`

ドキュメント: [Components Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#componentsObject)

* `schemas`: `User`や`Product`等のモデル
* `requestBodies`: リクエストボディ
* `responses`: APIレスポンス
* `headers`: リクエストヘッダ
* `parameters`: リクエストパラメータ

等、API定義で再利用可能なオブジェクトを定義できる。

`components`フィールドに定義したモデルは、`paths`で記述したように

```yaml
$ref: '#/components/schemas/User'
```

のように参照する。

```yaml:example.yaml
components:
  schemas: # スキーマオブジェクトの定義
    User: # モデル名
      type: object # 型
      required: # 必須フィールド
        - id
      properties:
        id: # プロパティ名
          type: integer # 型 
          format: int64 # フォーマット(int32, int64等)
        name:
          type: string
    Product:
      type: object
      required:
        - id
        - price
      properties:
        id:
          type: integer
          format: int64
          example: 1
        name:
          type: string
          example: Laptop
        price:
          type: integer
          example: 1200
```

Swagger UIでの表示
`Model`エリアに表示され、構造やサンプル値が参照できる。

<img width="815" alt="スクリーンショット 2018-09-08 14.37.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/241738/0c66fc57-f47a-a810-e7f8-7fbb2e4dabbe.png">


## `security`

ドキュメント: [Security Requirement Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#securityRequirementObject)

```yaml:example.yaml
security: 
  # Non-OAuth setting
  - api_key: []
  # OAuth setting
  - users_auth:
    - write:users
    - read:users
```

Swagger UIでの表示
メソッド定義部分に鍵マークが表示される。

<img width="809" alt="スクリーンショット 2018-09-08 14.01.45.png" src="https://　-image-store.s3.amazonaws.com/0/241738/08eb5273-e981-21a7-2b5e-eda188ae3a2d.png">


## `tags`

ドキュメント: [Tag Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#tagObject)

```yaml:example.yaml
tags:
  - name: users
    description: Access to Users
  - name: products
    description: Access to Products
```

|フィールド名|必須|概要|
|:--|:--|:--|
|name|**YES**|タグ名称。|
|description||タグの説明。|
|externalDocs||外部ドキュメント|

Swagger UIでの表示
各エンドポイントで`tags`に指定することで、定義をタグで分類できる。

<img width="814" alt="スクリーンショット 2018-09-08 15.22.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/241738/0f32affe-31e3-e91a-b08f-a54a0a3be009.png">


## `externalDocs`

ドキュメント: [External Documentation Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md#externalDocumentationObject)

```yaml:example.yaml
description: Find more info here
url: https://example.com
```

|フィールド名|必須|概要|
|:--|:--|:--|
|description||外部ドキュメントの説明。|
|url|**YES**|外部ドキュメントのURL。|

Swagger UIでの表示
ドキュメント上部にリンクが表示される。
<img width="833" alt="スクリーンショット 2018-09-08 13.51.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/241738/fbac4dfa-1c91-2697-b1ce-37a179f2af9f.png">

# まとめ
以上、OpenAPIの基本的な部分を紹介した。
OpenAPIはSwagger 2.0をベースにしているが、記法が異なる部分が多いため、公式ドキュメントをしっかり読み込んで行く必要がある。
手軽にRESTful APIの定義ができるうえ、Amazon API Gatewayと連携し定義のインポート/エクスポートが出来たりと非常に便利なため、ぜひ使ってみてほしい。

[Swagger 定義をインポートしてエッジ最適化 API をセットアップする](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/create-api-using-swagger.html)
[API Gateway から API をエクスポートする](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-export-api.html)

# 今回作成したサンプル

https://github.com/teinen/openapi-sample

# 参考

[Swagger Documentation](https://swagger.io/docs/)
[OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.0.md)