Swaggerの記法まとめ

本記事では、SwaggerのAPI定義を行うSwagger YAMLの記法についてまとめてみました。
使い初めはとっつきにくいSwaggerですが、この記事がSwaggerを使う方の参考になれば幸いです。

# Swagger Editorの紹介

Swagger YAMLを書く際には、[Swagger Editor](http://editor.swagger.io/#/)がとても便利です。
画面左側がエディター、右側がSwagger UIとなっておりリアルタイムで記法のチェックや定義書を確認できます。
Swaggerを書くエディタはいろいろありますが、気軽に記法を試す際にはSwagger Editorがとても便利です。
ぜひこれから紹介する記法を試す際にも、ぜひ使ってみてください。


# 初級編
## 基本の記述
初めにSwagger YAMLを記述するにあたり必須であるswagger, info, pathsについて説明します。
上記の基本的な構成で記述したシンプルなSwagger YAMLがこちらです。

```yaml
swagger: "2.0"
info:
  description: "これはペットストアに関するAPIです。"
  version: "1.0.0"
  title: "Petstore API"
  termsOfService: "http://swagger.io/terms/"
  contact:
    email: "apiteam@swagger.io"
  license:
    name: "Apache 2.0"
    url: "http://www.apache.org/licenses/LICENSE-2.0.html"
paths:
  /pet/{petId}:
    get:
      summary: "ペット情報API"
      description: "指定されたpetIdの情報を返します"
      parameters:
      - name: "petId"
        in: "path"
        description: "取得したいペットのID"
        required: true
        type: "integer"
        format: "int64"
      responses:
        200:
          description: "成功時のレスポンス"
          schema:
            type: "object"
            properties:
              id:
                type: "integer"
                format: "int64"
              name:
                type: "string"
                example: "doggie"

```
以降、このサンプルを基に記法を説明します。
実際に上記のSwagger YAMLをSwagger Editorに入力すると、画面右側にAPI定義書が下記のように作成されます。

![1.png](https://qiita-image-store.s3.amazonaws.com/0/63241/8f8be633-0496-8809-3b17-cf3be44b98d6.png)




それでは、 swagger, info, pathsそれぞれのオブジェクトの書き方について説明します。

![2.png](https://qiita-image-store.s3.amazonaws.com/0/63241/ebaf47aa-6bf5-8d1d-0e85-1035a686f114.png)


## swagger
swaggerには、Swaggerのバージョンを記述します。
変更する理由がない限りはここの値は `2.0`にしておきます。

## info
タイトル・説明・バージョンなど、APIについての情報を記載します。
infoには、以下の情報を記述できます。

| フィールド名 | 型 | 説明 | 必須|
|--------|--------|--------|--------|
|version|string|APIのバージョン|必須|
|title|string|ドキュメントのタイトル。一番上に表示される。|必須|
|description|string|ドキュメントについての説明||
|termsOfService|string|利用規約||
|contact|[contact object](https://swagger.io/specification/#contactObject)|APIについての問い合わせ先||
|license|[license object](https://swagger.io/specification/#licenseObject)|APIのライセンス||



## paths
APIのエントリポイントを記述します。
サンプルでは下記の部分にあたります。

![parameters.png](https://qiita-image-store.s3.amazonaws.com/0/63241/ce486b0c-5b5c-3e6e-f8c2-6c27d13a710c.png)


ここで定義する情報をもとにエントリポイントが作成されるため、pathsはとても重要なパートとなります。
pathsには、下記のような階層形式で情報を記載します。

- パスのURL(/pet/{PetId})
	- HTTPメソッド（get, postなど） 
		- エントリポイントのリクエストとレスポンスに関する記述

![request_response.png](https://qiita-image-store.s3.amazonaws.com/0/63241/c2e68d65-b5ef-611f-fdbe-299ad9874319.png)


### パスのURL
実際に定義したいエントリポイントのパスを記述します。
サンプルでいうところの `/pet/{petId}`です。

### HTTPメソッド（get, post, put, delete等）
パスの下には、パスのHTTPメソッドを記述します。
下記のように複数記述することもできます。

```yaml
  /pet/{petId}:
    get:
      summary: "ペット情報API"
      description: "指定されたpetIdの情報を返します"
      parameters: 省略
      responses: 省略　
    delete:
      summary: "ペット情報API"
      description: "指定されたpetIdを削除します"
      parameters: 省略
      responses: 省略
```

### エントリポイントのリクエストとレスポンスに関する記述
HTTPメソッドの下層には、エントリポイントがどのようなリクエストを受け取り、どのようなレスポンスを返すかを記述します。
サンプルでは、この部分にあたります。

![response.png](https://qiita-image-store.s3.amazonaws.com/0/63241/d0ca1c72-7d18-902a-602f-2681afcaa357.png)



設定できるフィールドは以下になります。

| フィールド名 | 型 | 説明 | 必須|
|--------|--------|--------|--------|
|responses|[response object](specification/#responsesObject)| 返ってくるレスポンス|必須|
|parameters|[parameter object](https://swagger.io/specification/#parameterObject) | リクエストのパラメーター||
|tags|array|swaggerオブジェクトで定義するどのtagに紐付けたいかを記述||
|summary|string|エントリポイントの概要（120文字以内）||
|description|string|エントリポイントの説明||
|consumes/produces|array|MIME Type||
|schemes|array|APIの通信プロトコル。必ずhttp, https, ws, wssの4種類のどれかを記述する。||
|security|[security requirement object](https://swagger.io/specification/#securityRequirementObject)|適用するセキュリティ||
|externalDocs|[external docs document](https://swagger.io/specification/#externalDocumentationObject)|外部リンク||
|deprecated|boolean|Deprecatedかどうかをtrueかfalseで記述。デフォルトはfalse。||

もっとも重要なparametersとresponsesについて補足説明します。

### parameters
リクエストの際に渡すパラメーターを記述します。
サンプルでは下記の部分にあたります。

![parameter.png](https://qiita-image-store.s3.amazonaws.com/0/63241/c09c67ea-4f59-9c20-2bc0-6af5e1d0e5b4.png)


記述するフィールドは以下です。

| フィールド名 | 型 | 説明 | 必須|
|--------|--------|--------|--------|
|name| string | パラメーター名|必須|
|in| string | パラメータの場所。query, header, path, formData, bodyの5種類のどれかを記述してください|必須|
|description| string | パラメータの説明||
|required|boolean | 必須パラメーターかどうかをtrueかfalseで記述||
|schema| [schema object](https://swagger.io/specification/#schemaObject) | bodyのパラメーターをスキーマオブジェクトとして記述。スキーマオブジェクトについては後述。inがbodyである場合に使用。|inがbodyである場合、必須|
|type| string | パラメーターのタイプ。<br>必ずstring, number, integer, boolean, array, fileの中から選ぶ。inがbody以外である場合に使用。|inがbody以外である場合、必須|
|format| string | パラメーターの型。[こちら](https://swagger.io/specification/#dataTypeFormat)から選ぶ。inがbody以外である場合に使用。||

typeとformatの指定は、[こちら](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types)が参考になります。

パラメーターが記述されると定義書ではこのように表示されます。

<img width="512" alt="parameter_example.png" src="https://qiita-image-store.s3.amazonaws.com/0/63241/18c56e20-dc3f-33ea-dfd9-46a0eae49a36.png">



### responses
返ってくるレスポンスを記述します。
サンプルでは下記の部分にあたります。

![response_example.png](https://qiita-image-store.s3.amazonaws.com/0/63241/47bc37b1-cb48-cace-67c2-9d1235acbe8a.png)



返したいHTTPステータスコードごとに、定義を行います。
定義できる設定は下記です。

| フィールド名 | 型 | 説明 | 必須|
|--------|--------|--------|--------|
|description| string | レスポンスの説明|必須|
|schema|[schema object](https://swagger.io/specification/#schemaObject) | レスポンスのbody。スキーマオブジェクトで記述する。スキーマオブジェクトについては後述。||
|headers|[headers object](https://swagger.io/specification/#headersObject) | レスポンスヘッダーを記述||
|example|[example object](https://swagger.io/specification/#exampleObject) | レスポンス例。レスポンスの値を自分で定義したいときに用いる。||

レスポンスを設定すると定義書が下記のように生成されます。

<img width="512" alt="response_definition.png" src="https://qiita-image-store.s3.amazonaws.com/0/63241/34880859-5207-d6a0-3693-95736a150172.png">



## スキーマオブジェクトについて
parameters, responsesを記述する際に、schemaを記述することができます。
このschemaにはスキーマオブジェクトを記述します。
スキーマオブジェクトは、bodyに用いるデータのタイプを定義することができるオブジェクトです。
主に配列かJSONオブジェクトを表現するときに使います。

### JSONオブジェクト

```yaml
schema:
  type: object
```
と指定すると、JSONオブジェクトを返すことができます。
例えば、APIのレスポンスを下記のように返したいとします。

```json
{
  "id": 1,
  "name": "doggie"
}
```

この場合はschemaをこのように書きます。

```yaml
schema:
  type: object
  properties:
    id:
      type: "integer"
      format: "int64"
      example: 1
    name:
      type: "string"
      example: "doggie"
```

`type: object`を指定した場合は、propertiesを設定します。
propertiesでは、カラムの情報を記述します。
propertiesには基本的に下記3つが記述されていれば動作します。

| フィールド名 | 型 | 説明 |
|--------|--------|--------|
|type|string|パラメーターのタイプ。必ずstring, number, integer, boolean, array, fileの中から選ぶ。|
|format|string| パラメーターの型。|
|example|typeで選んだ型|レスポンスで返したい文言|

typeとformatの指定は、[こちら](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types)が参考になります。


### 配列

```yaml
schema:
  type: array
```
と指定すると、配列を定義することができます。
例えば、APIのレスポンスを下記のように返したいとします。

```json
[
  {
    "id": 1,
    "name": "doggie"
  }
]
```

この場合はschemaをこのように書きます。

```yaml
schema:
  type: array
  items:
    type: "object"
    properties:
      id:
        type: "integer"
        format: "int64"
        example: 1
      name:
        type: "string"
        example: "doggie"
```

`type: array`を指定した場合は、itemsを設定します。
itemsには、配列の中のオブジェクトを記述します。
配列の中に、JSONを定義したい場合は、`type:object`を指定します。

```yaml
schema:
  type: array
  items:
    type: "object"
    properties:
      id:
        type: "integer"
        format: "int64"
        example: 1
      name:
        type: "string"
        example: "doggie"
```

また、配列の中に文字列を定義したい場合は、 `type: string`を記述します。

```yaml
schema:
  type: array
  items:
    type: "string"
```


# 中級編

## Swaggerを構成するオブジェクト
Swaggerは初級編で紹介したswagger, info, pathsも合わせ計15種類のオブジェクトから成り立っています。
多く感じられますが、すべてが必須というわけでなく、必須であるswagger, info, pathsが記載されていれば動きます。
必須以外のものは定義書に記載したいものがあれば記述します。

| フィールド名 | 型 | 説明 | 必須 |
|--------|--------|--------|--------|
| swagger | string | swaggerのバージョン。特に指定がなければ、デフォルトの2.0のままで大丈夫です。| 必須 |
| info | [info object](https://swagger.io/specification/#infoObject) | タイトル・説明・バージョンなど、APIについての情報を記載します。| 必須 |
|paths|[paths object](https://swagger.io/specification/#pathsObject)|提供するAPIのパスを書いていきます。Swagger定義の要です。|必須|
|host| string | API通信を行うサーバーのホスト。無記載であればドキュメントが動いているサーバーのホストとなります。||
|basePath| string | ホストに続くパス。スラッシュ（/）から始まる必要があります。無記載の場合、ホストの直下になります。<br>hostを`example.com`<br>basePathを`/v2`とした場合、APIのパスは`http://example.com/v2/〜`となります。||
|schemes|array|APIの通信プロトコル。必ずhttp, https, ws, wssの4種類のどれかを記載してください。無記載の場合、Swaggerの定義書がアクセスしているスキームになります。||
|produces|array|APIが提供できるMIME Typeの指定。選択肢は[こちら](https://swagger.io/specification/#mimeTypes)||
|consumes|array|APIが使用するMIME Typeの指定。選択肢は[こちら](https://swagger.io/specification/#mimeTypes)||
|definitions|[definitions object](https://swagger.io/specification/#definitionsObject)|レスポンスやパラメータに使用するデータ定義を記載します。||
|parameters|[parameters definitions object](https://swagger.io/specification/#parametersDefinitionsObject) |パラメーターを定義します。pathsの下に直接パラメーターを書くこともできますが、複数回同じパラメーターを使う場合に便利です。||
|responses|[responses definitions object](https://swagger.io/specification/#responsesDefinitionsObject) | レスポンスを定義します。pathsの下に直接レスポンスを書くこともできますが、複数回同じレスポンスを使う場合に便利です。||
|securityDefinitions|[security definitions object](https://swagger.io/specification/#securityDefinitionsObject)|セキュリティに関する定義を行えます。Oath認証についてもこちらで定義します。||
|security|[security requirement object](https://swagger.io/specification/#securityRequirementObject)|securityDefinitionsにて定義したセキュリティの中で何を適用するかを指定します。||
|tags|[[tag object](https://swagger.io/specification/#tagObject)]|タグの名前と説明を定義します。定義したタグをpathに紐付けることで、タグごとにpathがまとまりドキュメントが見やすくなります。||
|externalDocs|[external documentation object](https://swagger.io/specification/#externalDocumentationObject)|外部リンクを定義します。ドキュメントに貼りたいリンクがある場合、記載することでリンクが作成されます。||

それぞれのオブジェクトの記述は、[Swagger Editor](http://editor.swagger.io/)にデフォルトで入っているPet store APIがわかりやすく参考になります。

## definitionsを使う
同じスキーマオブジェクトを複数回使用したい場合、definitionsを使用しテンプレートとして定義することができます。

<img width="512" alt="definitions.png" src="https://qiita-image-store.s3.amazonaws.com/0/63241/eee3626c-da14-ee6f-3d83-b621dbf0c40c.png">


definitionsを使う際のポイントは、下記のようになります。

- テンプレート化したいスキーマオブジェクトを、definitionsに定義をする
- 呼び出したい箇所に、$refを使用して、definitionsのオブジェクトを呼び出す

サンプルをdefinitionsを用いて書き換えると以下のようになります。

```yaml
paths:
  /pet/{petId}:
    get:
    省略
      responses:
        200:
          description: "成功時のレスポンス"
          schema:
            $ref: "#/definitions/Pet" # definitionsで定義されたスキーマオブジェクトを呼び出す
definitions:
  Pet: # テンプレート名
    type: "object"
    properties:
      id:
        type: "integer"
        format: "int64"
      name:
        type: "string"
        example: "doggie"
```


definitionsは本当に便利です。
definitionsにレスポンスのスキーマを全て定義しpathsではそれらを呼び出すだけというような記述にすると、Swagger YAMLが見やすくなるのでオススメです。

## definitionsを複数呼び出す
definitionsにて定義したスキーマオブジェクトを複数呼び出すことも可能です。
下記のように、Storeの情報をdefinitionsで定義して呼び出してみます。

```yaml
paths:
  /pet/{petId}:
    get:
    省略
      responses:
        200:
          description: "成功時のレスポンス"
          schema:
            type: "object"
            properties:
              pet:
                $ref: "#/definitions/Pet" 
              store:
                $ref: "#/definitions/Store"
definitions:
  Pet: 
    type: "object"
    properties:
      id:
        type: "integer"
        format: "int64"
      name:
        type: "string"
        example: "doggie"
  Store:
    type: "object"
    properties:
      id:
        type: "integer"
        format: "int64"
        example: 1
      store_name:
        type: "string"
        example: "ABC PET STORE"
```

レスポンスはこのようになります。

```json
{
  "pet": {
    "id": 0,
    "name": "doggie"
  },
  "store": {
    "id": 1,
    "store_name": "ABC PET STORE"
  }
}
```


```yaml
schema:
  type: "object"
```
上記のように記述し、JSONオブジェクトのそれぞれのキーにdefinitionsで定義したスキーマオブジェクトを呼び出します。

# 上級編


## definitionsを入れ子にする
中級編では、レスポンスにdefinitionsで定義したスキーマオブジェクトを呼び出しました。
definitionsはスキーマオブジェクトを定義し、呼び出すことができるのでschemaが使えるところではどこでも呼び出すことができます。

例えば、definitionsの中でスキーマオブジェクトを呼び出すことも可能です。

```yaml
paths:
  /pet/{petId}:
    get:
    省略
      responses:
        200:
          description: "成功時のレスポンス"
          schema:
            $ref: "#/definitions/Pet" 
definitions:
  Pet: 
    type: "object"
    properties:
      id:
        type: "integer"
        format: "int64"
      name:
        type: "string"
        example: "doggie"
      stores:
        type: "array"
        items:
          $ref: "#/definitions/Store" # Storeを呼び出す
  Store:
    type: "object"
    properties:
      id:
        type: "integer"
        format: "int64"
        example: 1
      store_name:
        type: "string"
        example: "ABC PET STORE"
```

上記のようにレスポンスを定義した場合、レスポンスはこのように生成されます。

```json
{
  "id": 0,
  "name": "doggie",
  "stores": [
    {
      "id": 1,
      "store_name": "ABC PET STORE"
    }
  ]
}
```

storesの配列の中で、itemsの1つとしてStoreオブジェクトを呼び出しています。


# Swagger YAMLを書く上で気をつけたい点
ここからは、実際に私がSwagger YAMLを書く上でうまくいかなかったりつまづいた点を共有の意味を込めてご紹介します。

## レスポンスの配列に複数データを入れたい
通常レスポンスに配列を定義する際は、このように記述します。

```yaml
schema:
  type: array
  items:
    type: "object"
    properties:
      id:
        type: "integer"
        format: "int64"
        example: 1
      name:
        type: "string"
        example: "doggie"
```

この記述で返ってくるレスポンスは`[ { id: 1, name: "doggie" } ]`と要素が1つしか返ってきません。
2つ以上の配列要素を追加したい場合は、オブジェクト自体のexampleを記述します。

```yaml
paths:
  /pet/{petId}:
    get:
    省略
      responses:
        200:
          description: "成功時のレスポンス"
          schema:
            type: "object"
            properties:
              pet:
                $ref: "#/definitions/Pet" 

definitions:
  Pet: 
    type: "array"
    items:
      example:
        - id: 1
          name: "doggie"
        - id: 2
          name: "pochi"
```

## formatを指定しても、デフォルトで値が入らない

[Swaggerのドキュメント](https://swagger.io/specification/#dataTypeFormat)には、スキーマオブジェクトのformatにemailやuuidを指定するとexampleを設定しなくても値が入るという記述があります。
実際、Swagger Editorでは下記のようにデフォルトで値が入ります。

<img width="512" alt="format.png" src="https://qiita-image-store.s3.amazonaws.com/0/63241/aa270762-5ce8-f9f2-7dfd-eb4527cb8d6a.png">


しかし、Swagger Codegenを使ってモックアプリケーションを生成する場合には、formatを指定してもデフォルトで値が入りません。
formatのデフォルト値を使用する際は、ご自分の環境でデフォルト値が入るかどうかを確認してから使う方が良いと思います。

## Amazon API Gatewayではexampleが使えない
Swaggerを用いてAPIを生成するサービスの1つに、[Amazon API Gateway](https://aws.amazon.com/jp/api-gateway/)があります。
API Gatewayは、Swagger YAMLをインポートすることで簡単にAPIを作成することができます。
API Gatewayを用いてAPIを作成する方法については、下記の2つが参考になるのでぜひ読んでみてください。

[例から API Gateway API を作成する - Amazon API Gateway](http://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-create-api-from-example.html)
[開発効率を上げる！Swaggerで作るWEB APIモック - VASILY DEVELOPERS BLOG](http://tech.vasily.jp/entry/swagger_api_mock)

API GatewayにSwagger YAMLをインポートしてモックアプリケーションを作成する場合は、exampleが使えません。
exampleが記述されているSwagger YAMLをAPI Gatewayでロードすると`Invalid model schema specified`というエラーが返ってきます。
API Gatewayでレスポンス例を記述したい時は、下記のように総合レスポンスの設定時に記述しましょう。

<img width="512" alt="aws.png" src="https://qiita-image-store.s3.amazonaws.com/0/63241/c1a1fec6-e73a-2742-bf8e-e9ba84aea5bd.png">



# まとめ
以上、基本的なSwagger YAMLの書き方についてご紹介しました！
Swaggerは高機能であるゆえに、最初はとてもとっつきにくく感じてしまいます。
しかし一度覚えてしまえば、Swaggerは開発効率を上げることができるとても便利なフレームワークです。
この記事では紹介しきれなかった機能も多くあるので、[公式ドキュメント](https://swagger.io/specification/)を参考にしてみてください。



# 参考
- [petstore.yaml](https://github.com/swagger-api/swagger-codegen/blob/master/modules/swagger-codegen/src/test/resources/2_0/petstore.yaml)
- [Swagger Specification](https://swagger.io/specification/)