ローカル環境にSwaggerでWebAPIドキュメントとWebAPIモックサーバーを用意する方法

## ローカル環境にSwaggerでAPIドキュメントとモックサーバーを用意する方法

### 1. 使用ツール

Spotlight Studio ... UIでAPI仕様を定義でき、Json形式のOpenAPI(Swagger)のAPI仕様(Swaggerファイル)をファイル出力することができる。

swagger-ui ... SwaggerファイルをWebページとしてファイル出力できる。

phpのWeb出力ツール(php -S localhost:3000 -t ./) ... ローカル環境にあるファイルをWeb出力できる。Mac環境だと標準でphp環境が入っているので初めから入っている。

Prism ... Swaggerファイルを実際にMockサーバーとして動かすことができる。

### 2. 環境構築の前提条件

a. node.js(javascriptで作られたツールのパッケージ管理ソフト、使用するツールをこれで取る)をインストール済み。最新でも問題ないと思いますが念のため、使用しているバージョンは、node-v14.17.4.pkgです。<a href="https://nodejs.org/ja/download/">ダウンロードページ</a>

b. githubのツールをインストール済み。Xcodeを使っている人はXcodeを入れたタイミングでダウンロードされます。必須ではなく、直接githubのページからzipファイルをダウンロードして解凍でも問題なし。

### 3. ローカル環境の構築

a-1. SwaggerファイルをSpotlight Studioで用意する。使用しているバージョンはstoplight-studio-mac.dmgの2.8.0です。<a href="https://stoplight.io/studio/">ダウンロードページ</a>

a-2. プロジェクトを作成してAPIとエンドポイントを作成します。今回はAPI定義済みの外部ファイルを直接入れて説明を割愛します。

###### 自分で作る場合の注意点
・OpenAPIのバージョンが3.1だと消せないエラーが出るので注意が必要です。
・プロジェクト自体はツールの中に作成されます。ファイルをこのツールを使って編集という使い方ができないので編集の必要がある場合、ファイルを取り込んで編集し、編集したファイルをjsonで出力する必要がある。

プロジェクト名(Swaggerファイルになる)を入力する。

![スクリーンショット 2022-01-16 18.27.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/6e8f94b0-67e2-0ed2-7505-91a1b101e42b.png)

Import Fileを選択(ここでAPIとエンドポイントを作成してもよい。エラー解消の説明があるので今回はエラーを消したファイルのインポートで割愛する)

![スクリーンショット 2022-01-16 18.28.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/f68b7d53-0143-70d2-4262-789052394935.png)

エラーを消した、下記ファイルを追加する。

```json:ArchitectureIOs.json
{
  "openapi": "3.0.0",
  "info": {
    "title": "ArchitectureIOs",
    "version": "1.0",
    "description": "ArchitectureIOs用のAPIモック",
    "contact": {
      "name": "脇坂亮汰"
    }
  },
  "servers": [
    {
      "url": "http://localhost:3000"
    }
  ],
  "paths": {
    "/users/{userId}": {
      "parameters": [
        {
          "schema": {
            "type": "integer"
          },
          "name": "userId",
          "in": "path",
          "required": true,
          "description": "Id of an existing user."
        }
      ],
      "get": {
        "summary": "Get User Info by User ID",
        "tags": [],
        "responses": {
          "200": {
            "description": "User Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                },
                "examples": {
                  "Get User Alice Smith": {
                    "value": {
                      "id": 142,
                      "firstName": "Alice",
                      "lastName": "Smith",
                      "email": "alice.smith@gmail.com",
                      "dateOfBirth": "1997-10-31",
                      "emailVerified": true,
                      "signUpDate": "2019-08-24"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "User Not Found"
          }
        },
        "operationId": "get-users-userId",
        "description": "Retrieve the information of the user with the matching user ID."
      },
      "patch": {
        "summary": "Update User Information",
        "tags": [],
        "operationId": "patch-users-userId",
        "responses": {
          "200": {
            "description": "User Updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                },
                "examples": {
                  "Updated User Rebecca Baker": {
                    "value": {
                      "id": 13,
                      "firstName": "Rebecca",
                      "lastName": "Baker",
                      "email": "rebecca@gmail.com",
                      "dateOfBirth": "1985-10-02",
                      "emailVerified": false,
                      "createDate": "2019-08-24"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "User Not Found"
          },
          "409": {
            "description": "Email Already Taken"
          }
        },
        "description": "Update the information of an existing user.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "firstName": {
                    "type": "string"
                  },
                  "lastName": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "description": "If a new email is given, the user's email verified property will be set to false."
                  },
                  "dateOfBirth": {
                    "type": "string"
                  }
                }
              },
              "examples": {
                "Update First Name": {
                  "value": {
                    "firstName": "Rebecca"
                  }
                },
                "Update Email": {
                  "value": {
                    "email": "rebecca@gmail.com"
                  }
                },
                "Update Last Name & Date of Birth": {
                  "value": {
                    "lastName": "Baker",
                    "dateOfBirth": "1985-10-02"
                  }
                }
              }
            }
          },
          "description": "Patch user properties to update."
        }
      }
    },
    "/user": {
      "post": {
        "summary": "Create New User",
        "tags": [],
        "operationId": "post-user",
        "responses": {
          "200": {
            "description": "User Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                },
                "examples": {
                  "New User Bob Fellow": {
                    "value": {
                      "id": 12,
                      "firstName": "Bob",
                      "lastName": "Fellow",
                      "email": "bob.fellow@gmail.com",
                      "dateOfBirth": "1996-08-24",
                      "emailVerified": false,
                      "createDate": "2020-11-18"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing Required Information"
          },
          "409": {
            "description": "Email Already Taken"
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "firstName": {
                    "type": "string"
                  },
                  "lastName": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "dateOfBirth": {
                    "type": "string",
                    "format": "date"
                  }
                },
                "required": [
                  "firstName",
                  "lastName",
                  "email",
                  "dateOfBirth"
                ]
              },
              "examples": {
                "Create User Bob Fellow": {
                  "value": {
                    "firstName": "Bob",
                    "lastName": "Fellow",
                    "email": "bob.fellow@gmail.com",
                    "dateOfBirth": "1996-08-24"
                  }
                }
              }
            }
          },
          "description": "Post the necessary fields for the API to create a new user."
        },
        "description": "Create a new user."
      }
    }
  },
  "components": {
    "schemas": {
      "User": {
        "title": "User",
        "type": "object",
        "description": "",
        "x-examples": {
          "Alice Smith": {
            "id": 142,
            "firstName": "Alice",
            "lastName": "Smith",
            "email": "alice.smith@gmail.com",
            "dateOfBirth": "1997-10-31",
            "emailVerified": true,
            "signUpDate": "2019-08-24"
          }
        },
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier for the given user."
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "dateOfBirth": {
            "type": "string",
            "format": "date",
            "example": "1997-10-31"
          },
          "emailVerified": {
            "type": "boolean",
            "description": "Set to true if the user's email has been verified."
          },
          "createDate": {
            "type": "string",
            "format": "date",
            "description": "The date that the user was created."
          }
        },
        "required": [
          "id",
          "firstName",
          "lastName",
          "email",
          "emailVerified"
        ]
      }
    }
  }
}
```

![スクリーンショット 2022-01-16 18.29.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/c7ca9820-7d24-ddda-9473-6a8ba11d93de.png)

内容を編集してExportする。今回は何もしない。

a-3. swagger-ui(Webドキュメント)環境を作成する。git cloneは直接ブラウザでチェックアウト先のURLからzipファイルをダウンロードで代用できる。

```bash
$ git clone https://github.com/swagger-api/swagger-ui.git
$ cd swagger-ui
$ npm install
$ npm run build
```

a-4. 両方Webでローカル環境に公開したいため、Swaggerファイル(ArchitectureIOs.json)と構築したswagger-uiフォルダを同じ階層にする。

![スクリーンショット 2022-01-16 18.57.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/3d22c778-8550-9157-4b94-50e8168f1ce3.png)

a-5. swagger-uiの読み込むjsonファイルのパスを、配置したArchitectureIOs.jsonに変更する。

変更ファイル：./swagger-ui/dist/index.html
変更箇所：SwaggerUIBundle({のurl
変更後：http://localhost:3000/ArchitectureIOs.json

```bash
$ vi swagger-ui/dist/index.html
```

![スクリーンショット 2022-01-16 19.00.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/caa9f308-fc2b-3d70-3c20-85d08cb255cc.png)

a-6. Webサーバーを起動する。下記はドメインをlocalhost:3000でカレントディレクトリをWebサーバーに公開する。

```bash
$ php -S localhost:3000 -t ./
```

![スクリーンショット 2022-01-16 19.07.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/ecd56ba7-8157-b130-afdd-d35336a485dd.png)


a-7. swagger-uiにアクセスするため、http://localhost:3000/swagger-ui/dist/ にWebブラウザでアクセスする。

![スクリーンショット 2022-01-16 19.10.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/7490dbd8-99c6-4733-e60e-95f1be4be7b5.png)

a-8. Prism(モックサーバー)環境を構築する。

```bash
$ sudo npm install -g @stoplight/prism-cli
```

a-9. Prismを起動する。

```bash
$ npx prism mock ./ArchitectureIOs.json -p 3000
```

![スクリーンショット 2022-01-16 19.16.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/599cb2cd-3b80-b976-3d9a-1df0e07921d1.png)

a-10. curlでAPIの戻りを確認する。

```bash
$ curl -X 'POST' \
  'http://127.0.0.1:3000/user' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "firstName": "Bob",
  "lastName": "Fellow",
  "email": "bob.fellow@gmail.com",
  "dateOfBirth": "1996-08-24"
}'
```

![スクリーンショット 2022-01-16 19.18.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/4c0734fa-0ddc-c52e-58f3-e4486a91eb0d.png)

a-11. Prismが不要になった場合はアンインストールする。

```bash
$ sudo npm uninstall -g @stoplight/prism-cli
```