OpenAPI に複数のレスポンス例を書いて、prism で mock API を立ててレスポンスを切り替えると便利

# はじめに
OpenAPI に複数のレスポンス例を書いて、prism で mock API を立てて、レスポンスを切り替えながら GUI 開発をすると便利と実感したので共有します。

検証に使ったコードはこちらに置いてあります。

https://github.com/nnashiki/openapi-mock-practice

# 目次

- [OpenAPI Specification の仕様](#OpenAPISpecificationの仕様)
- [Prism の仕様](#prism-の仕様)
- [開発時に行うこと](#開発時に行うこと)
- [用意したコード](#用意したコード)
    - [Caddyfile](#caddyfile)
    - [docker-compose.yaml](#docker-composeyaml)
    - [openapi.yaml](#openapiyaml)

- [考察](#考察)
- [検証の元になった記事](#検証の元になった記事)
- [最後に](#最後に)

# OpenAPISpecificationの仕様

OpenAPI Specification は複数のレスポンス例を書くことができます。swagger UI を使えば複数のレスポンス例を切り替えて表示できます。

- ![スクリーンショット 2024-07-05 9.01.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/2b582454-f510-60d7-812d-2f2ade31c02f.png)
- ![スクリーンショット 2024-07-05 9.02.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/9dfba4b8-1588-34a0-9413-0948866683cd.png)



https://swagger.io/docs/specification/adding-examples/


# Prism の仕様

Prism は OpenAPI に沿った mock server を立ててくれるツールです。Prism は request header に Prefer を入れることで response 内容を切り替えてくれます。例えば、"Prefer: example=example1" と指定すると example1 の response body を返してくれます。また、"Prefer: code=500" と指定すると status code 500 に記載してある example の response body を返してくれます。

https://docs.stoplight.io/docs/prism/674b27b261c3c-prism-overview

# 開発時に行うこと

1. OpenAPI に欲しいケースのレスポンス例を複数追加します。
2. Prism で mock server を立ち上げます。
3. request header に Prefer を付けてレスポンスを切り替えます。
    - local動作確認でmockが欲しい場合は load balancer(Caddy) を経由して header を追加します。

以下は、mock API を直接叩いた場合と、load balancer(Caddy)で header を追加した場合のレスポンスが異なる例です:

```
$ curl http://localhost:4010/api/users -s | jq       
{
  "users": []
}

$ curl http://localhost:8000/api/users -s | jq
{
  "users": [
    {
      "id": 1,
      "name": "John Doe",
      "mailAddress": "john.doe@example.com"
    },
    {
      "id": 2,
      "name": "Jane Doe",
      "mailAddress": "jane.doe@example.com"
    }
  ]
}
```

# 用意したコード

## Caddyfile

```
http://localhost:80 {
    # API の定義を記載 start

    @api_users_get {
        path /api/users
        method GET
    }
    # API の定義を記載 end

    # レスポンス切り替える対象を記載 start
    handle @api_users_get {
        reverse_proxy http://api-mock:4010 {
            header_up Prefer "example=ManyUserResponse"
        }
    }
    # レスポンス切り替える対象を記載 end

    handle {
        reverse_proxy http://api-mock:4010
    }
}
```

## docker-compose.yaml

```
version: '3.9'

services:
  api-mock-proxy:
    image: caddy:2
    restart: always
    ports:
      - "8000:80"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
    depends_on:
      api-mock:
        condition: service_started

  api-mock:
    image: stoplight/prism:4
    ports:
      - "4010:4010"
    volumes:
      - ./openapi.yaml:/openapi.yaml
    command:
      mock /openapi.yaml -h 0.0.0.0 -p 4010

  swagger:
    image: swaggerapi/swagger-editor
    ports:
      - "8080:8080"
    volumes:
      - ./openapi.yaml:/openapi.yaml
    environment:
     - SWAGGER_FILE=/openapi.yaml
```

## openapi.yaml

```
openapi: 3.0.3
info:
  title: User API
  version: 1.0.0
paths:
  /api/users:
    get:
      summary: Get list of users
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  users:
                    type: array
                    items:
                      $ref: '#/components/schemas/User'
              examples:
                emptyUsersResponse:
                  summary: No users
                  value:
                    users: []
                oneUserResponse:
                  summary: One user
                  value:
                    users:
                      - id: 1
                        name: John Doe
                        mailAddress: john.doe@example.com
                ManyUserResponse:
                  summary: Multiple users
                  value:
                    users:
                      - id: 1
                        name: John Doe
                        mailAddress: john.doe@example.com
                      - id: 2
                        name: Jane Doe
                        mailAddress: jane.doe@example.com
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
              example:
                error: Internal server error
components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: John Doe
        mailAddress:
          type: string
          example: john.doe@example.com
```

# 考察

mswjs でレスポンスを変更してテストする方法もあるみたいです。しかし、本記事の方法だとOpenAPI定義書に複数のレスポンス例が書かれて良いです。

https://mswjs.io/

# 検証の元になった記事

https://blog.tech-monex.com/entry/2022/12/23/100208

# 最後に

最後までお読み頂きありがとうございます。励みになるので、良かったらいいね 👍 をお願い致します。