OpenAPI ドキュメントを CloudFront + S3 上でベーシック認証付きでホスティングして Swagger UI で視覚化する構成を AWS CDK で実装してみた

OpenAPI ドキュメントを CloudFront + S3 上でベーシック認証付きでホスティングして Swagger UI で視覚化する構成を AWS CDK で実装してみた

Clock Icon2025.07.10

こんにちは、製造ビジネステクノロジー部の若槻です。

OpenAPI Specification(OAS)に基づいた Yaml や Json 形式のドキュメントを HTML 形式で視覚化する UI として広く使われているのが Swagger UI です。

https://github.com/swagger-api/swagger-ui

さて OAS ドキュメントを Swagger UI で簡単に表示する方法として Swagger Viewer のような VS Code 拡張機能を使う方法がありますが、プロジェクトの内外のメンバーにさらに簡単に API 仕様を共有するために Web 上で Swagger UI で公開したいニーズも多いのではないでしょうか。

今回は、OAS ドキュメントを CloudFront + S3 上でベーシック認証付きでホスティングして Swagger UI で視覚化する構成を AWS CDK で実装してみました。

やってみた

サンプルの OAS ドキュメントを配置

下記で公開されているサンプルの OAS ドキュメント(ペットストア)を、docs/api/petstore.yaml に配置します。

https://raw.githubusercontent.com/openapitools/openapi-generator/master/modules/openapi-generator/src/test/resources/3_0/petstore.yaml

Petstore OpenAPI ドキュメント
docs/api/petstore.yaml
openapi: 3.0.0
servers:
  - url: "http://petstore.swagger.io/v2"
info:
  description: >-
    This is a sample server Petstore server. For this sample, you can use the api key
    `special-key` to test the authorization filters.
  version: 1.0.0
  title: OpenAPI Petstore
  license:
    name: Apache-2.0
    url: "https://www.apache.org/licenses/LICENSE-2.0.html"
tags:
  - name: pet
    description: Everything about your Pets
  - name: store
    description: Access to Petstore orders
  - name: user
    description: Operations about user
paths:
  /pet:
    post:
      tags:
        - pet
      summary: Add a new pet to the store
      description: ""
      operationId: addPet
      responses:
        "200":
          description: successful operation
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/Pet"
            application/json:
              schema:
                $ref: "#/components/schemas/Pet"
        "405":
          description: Invalid input
      security:
        - petstore_auth:
            - "write:pets"
            - "read:pets"
      requestBody:
        $ref: "#/components/requestBodies/Pet"
    put:
      tags:
        - pet
      summary: Update an existing pet
      description: ""
      operationId: updatePet
      externalDocs:
        url: "http://petstore.swagger.io/v2/doc/updatePet"
        description: "API documentation for the updatePet operation"
      responses:
        "200":
          description: successful operation
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/Pet"
            application/json:
              schema:
                $ref: "#/components/schemas/Pet"
        "400":
          description: Invalid ID supplied
        "404":
          description: Pet not found
        "405":
          description: Validation exception
      security:
        - petstore_auth:
            - "write:pets"
            - "read:pets"
      requestBody:
        $ref: "#/components/requestBodies/Pet"
  /pet/findByStatus:
    get:
      tags:
        - pet
      summary: Finds Pets by status
      description: Multiple status values can be provided with comma separated strings
      operationId: findPetsByStatus
      parameters:
        - name: status
          in: query
          description: Status values that need to be considered for filter
          required: true
          style: form
          explode: false
          deprecated: true
          schema:
            type: array
            items:
              type: string
              enum:
                - available
                - pending
                - sold
              default: available
      responses:
        "200":
          description: successful operation
          content:
            application/xml:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Pet"
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Pet"
        "400":
          description: Invalid status value
      security:
        - petstore_auth:
            - "read:pets"
  /pet/findByTags:
    get:
      tags:
        - pet
      summary: Finds Pets by tags
      description: >-
        Multiple tags can be provided with comma separated strings. Use tag1,
        tag2, tag3 for testing.
      operationId: findPetsByTags
      parameters:
        - name: tags
          in: query
          description: Tags to filter by
          required: true
          style: form
          explode: false
          schema:
            type: array
            items:
              type: string
      responses:
        "200":
          description: successful operation
          content:
            application/xml:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Pet"
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Pet"
        "400":
          description: Invalid tag value
      security:
        - petstore_auth:
            - "read:pets"
      deprecated: true
  "/pet/{petId}":
    get:
      tags:
        - pet
      summary: Find pet by ID
      description: Returns a single pet
      operationId: getPetById
      parameters:
        - name: petId
          in: path
          description: ID of pet to return
          required: true
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: successful operation
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/Pet"
            application/json:
              schema:
                $ref: "#/components/schemas/Pet"
        "400":
          description: Invalid ID supplied
        "404":
          description: Pet not found
      security:
        - api_key: []
    post:
      tags:
        - pet
      summary: Updates a pet in the store with form data
      description: ""
      operationId: updatePetWithForm
      parameters:
        - name: petId
          in: path
          description: ID of pet that needs to be updated
          required: true
          schema:
            type: integer
            format: int64
      responses:
        "405":
          description: Invalid input
      security:
        - petstore_auth:
            - "write:pets"
            - "read:pets"
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                name:
                  description: Updated name of the pet
                  type: string
                status:
                  description: Updated status of the pet
                  type: string
    delete:
      tags:
        - pet
      summary: Deletes a pet
      description: ""
      operationId: deletePet
      parameters:
        - name: api_key
          in: header
          required: false
          schema:
            type: string
        - name: petId
          in: path
          description: Pet id to delete
          required: true
          schema:
            type: integer
            format: int64
      responses:
        "400":
          description: Invalid pet value
      security:
        - petstore_auth:
            - "write:pets"
            - "read:pets"
  "/pet/{petId}/uploadImage":
    post:
      tags:
        - pet
      summary: uploads an image
      description: ""
      operationId: uploadFile
      parameters:
        - name: petId
          in: path
          description: ID of pet to update
          required: true
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponse"
      security:
        - petstore_auth:
            - "write:pets"
            - "read:pets"
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                additionalMetadata:
                  description: Additional data to pass to server
                  type: string
                file:
                  description: file to upload
                  type: string
                  format: binary
  /store/inventory:
    get:
      tags:
        - store
      summary: Returns pet inventories by status
      description: Returns a map of status codes to quantities
      operationId: getInventory
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: integer
                  format: int32
      security:
        - api_key: []
  /store/order:
    post:
      tags:
        - store
      summary: Place an order for a pet
      description: ""
      operationId: placeOrder
      responses:
        "200":
          description: successful operation
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/Order"
            application/json:
              schema:
                $ref: "#/components/schemas/Order"
        "400":
          description: Invalid Order
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Order"
        description: order placed for purchasing the pet
        required: true
  "/store/order/{orderId}":
    get:
      tags:
        - store
      summary: Find purchase order by ID
      description: >-
        For valid response try integer IDs with value <= 5 or > 10. Other values
        will generate exceptions
      operationId: getOrderById
      parameters:
        - name: orderId
          in: path
          description: ID of pet that needs to be fetched
          required: true
          schema:
            type: integer
            format: int64
            minimum: 1
            maximum: 5
      responses:
        "200":
          description: successful operation
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/Order"
            application/json:
              schema:
                $ref: "#/components/schemas/Order"
        "400":
          description: Invalid ID supplied
        "404":
          description: Order not found
    delete:
      tags:
        - store
      summary: Delete purchase order by ID
      description: >-
        For valid response try integer IDs with value < 1000. Anything above
        1000 or nonintegers will generate API errors
      operationId: deleteOrder
      parameters:
        - name: orderId
          in: path
          description: ID of the order that needs to be deleted
          required: true
          schema:
            type: string
      responses:
        "400":
          description: Invalid ID supplied
        "404":
          description: Order not found
  /user:
    post:
      tags:
        - user
      summary: Create user
      description: This can only be done by the logged in user.
      operationId: createUser
      responses:
        default:
          description: successful operation
      security:
        - api_key: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/User"
        description: Created user object
        required: true
  /user/createWithArray:
    post:
      tags:
        - user
      summary: Creates list of users with given input array
      description: ""
      operationId: createUsersWithArrayInput
      responses:
        default:
          description: successful operation
      security:
        - api_key: []
      requestBody:
        $ref: "#/components/requestBodies/UserArray"
  /user/createWithList:
    post:
      tags:
        - user
      summary: Creates list of users with given input array
      description: ""
      operationId: createUsersWithListInput
      responses:
        default:
          description: successful operation
      security:
        - api_key: []
      requestBody:
        $ref: "#/components/requestBodies/UserArray"
  /user/login:
    get:
      tags:
        - user
      summary: Logs user into the system
      description: ""
      operationId: loginUser
      parameters:
        - name: username
          in: query
          description: The user name for login
          required: true
          schema:
            type: string
            pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$'
        - name: password
          in: query
          description: The password for login in clear text
          required: true
          schema:
            type: string
      responses:
        "200":
          description: successful operation
          headers:
            Set-Cookie:
              description: >-
                Cookie authentication key for use with the `api_key`
                apiKey authentication.
              schema:
                type: string
                example: AUTH_KEY=abcde12345; Path=/; HttpOnly
            X-Rate-Limit:
              description: calls per hour allowed by the user
              schema:
                type: integer
                format: int32
            X-Expires-After:
              description: date in UTC when token expires
              schema:
                type: string
                format: date-time
          content:
            application/xml:
              schema:
                type: string
            application/json:
              schema:
                type: string
        "400":
          description: Invalid username/password supplied
  /user/logout:
    get:
      tags:
        - user
      summary: Logs out current logged in user session
      description: ""
      operationId: logoutUser
      responses:
        default:
          description: successful operation
      security:
        - api_key: []
  "/user/{username}":
    get:
      tags:
        - user
      summary: Get user by user name
      description: ""
      operationId: getUserByName
      parameters:
        - name: username
          in: path
          description: The name that needs to be fetched. Use user1 for testing.
          required: true
          schema:
            type: string
      responses:
        "200":
          description: successful operation
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/User"
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "400":
          description: Invalid username supplied
        "404":
          description: User not found
    put:
      tags:
        - user
      summary: Updated user
      description: This can only be done by the logged in user.
      operationId: updateUser
      parameters:
        - name: username
          in: path
          description: name that need to be deleted
          required: true
          schema:
            type: string
      responses:
        "400":
          description: Invalid user supplied
        "404":
          description: User not found
      security:
        - api_key: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/User"
        description: Updated user object
        required: true
    delete:
      tags:
        - user
      summary: Delete user
      description: This can only be done by the logged in user.
      operationId: deleteUser
      parameters:
        - name: username
          in: path
          description: The name that needs to be deleted
          required: true
          schema:
            type: string
      responses:
        "400":
          description: Invalid username supplied
        "404":
          description: User not found
      security:
        - api_key: []
externalDocs:
  description: Find out more about Swagger
  url: "http://swagger.io"
components:
  requestBodies:
    UserArray:
      content:
        application/json:
          schema:
            type: array
            items:
              $ref: "#/components/schemas/User"
      description: List of user object
      required: true
    Pet:
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Pet"
        application/xml:
          schema:
            $ref: "#/components/schemas/Pet"
      description: Pet object that needs to be added to the store
      required: true
  securitySchemes:
    petstore_auth:
      type: oauth2
      flows:
        implicit:
          authorizationUrl: "http://petstore.swagger.io/api/oauth/dialog"
          scopes:
            "write:pets": modify pets in your account
            "read:pets": read your pets
    api_key:
      type: apiKey
      name: api_key
      in: header
  schemas:
    Order:
      title: Pet Order
      description: An order for a pets from the pet store
      type: object
      properties:
        id:
          type: integer
          format: int64
        petId:
          type: integer
          format: int64
        quantity:
          type: integer
          format: int32
        shipDate:
          type: string
          format: date-time
        status:
          type: string
          description: Order Status
          enum:
            - placed
            - approved
            - delivered
        complete:
          type: boolean
          default: false
      xml:
        name: Order
    Category:
      title: Pet category
      description: A category for a pet
      type: object
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
          pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$'
      xml:
        name: Category
    User:
      title: a User
      description: A User who is purchasing from the pet store
      type: object
      properties:
        id:
          type: integer
          format: int64
        username:
          type: string
        firstName:
          type: string
        lastName:
          type: string
        email:
          type: string
        password:
          type: string
        phone:
          type: string
        userStatus:
          type: integer
          format: int32
          description: User Status
      xml:
        name: User
    Tag:
      title: Pet Tag
      description: A tag for a pet
      type: object
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
      xml:
        name: Tag
    Pet:
      title: a Pet
      description: A pet for sale in the pet store
      type: object
      required:
        - name
        - photoUrls
      properties:
        id:
          type: integer
          format: int64
        category:
          $ref: "#/components/schemas/Category"
        name:
          type: string
          example: doggie
        photoUrls:
          type: array
          xml:
            name: photoUrl
            wrapped: true
          items:
            type: string
        tags:
          type: array
          xml:
            name: tag
            wrapped: true
          items:
            $ref: "#/components/schemas/Tag"
        status:
          type: string
          description: pet status in the store
          deprecated: true
          enum:
            - available
            - pending
            - sold
      xml:
        name: Pet
    ApiResponse:
      title: An uploaded response
      description: Describes the result of uploading an image resource
      type: object
      properties:
        code:
          type: integer
          format: int32
        type:
          type: string
        message:
          type: string

Swagger UI の HTML を配置

下記のドキュメントで Swagger UI のコード HTML に unpkg を使って直接埋め込むサンプルが紹介されているので、これを参考にして docs/api/index.html を作成します。

https://swagger.io/docs/open-source-tools/swagger-ui/usage/installation/#unpkg

docs/api/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="description" content="SwaggerUI" />
    <title>SwaggerUI</title>
    <link
      rel="stylesheet"
      href="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css"
    />
  </head>
  <body>
    <div id="swagger-ui"></div>
    <script
      src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-bundle.js"
      crossorigin
    ></script>
    <script>
      window.onload = () => {
        window.ui = SwaggerUIBundle({
          url: "./petstore.yaml",
          dom_id: "#swagger-ui",
        });
      };
    </script>
  </body>
</html>

CDK コード

CloudFront + S3 上でベーシック認証付きでホスティングするための CDK コードです。ベーシック認証はこちらを参考に CloudFront Function と KeyValueStore を使って実装しています。

lib/main-stack.ts
import * as cdk from "aws-cdk-lib";
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import * as cloudfront_origins from "aws-cdk-lib/aws-cloudfront-origins";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as s3_deployment from "aws-cdk-lib/aws-s3-deployment";
import { Construct } from "constructs";

export class MainStack extends cdk.Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    // S3 バケット
    const bucket = new s3.Bucket(this, "Bucket", {
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
    });

    // Basic認証のための KeyValueStore の作成
    const keyValueStore = new cloudfront.KeyValueStore(this, "KeyValueStore");

    // KeyValueStore の ARN を出力
    new cdk.CfnOutput(this, "KeyValueStoreArn", {
      value: keyValueStore.keyValueStoreArn,
    });

    // Basic認証を行う CloudFront Function の作成
    const cloudfrontFunction = new cloudfront.Function(this, "Function", {
      keyValueStore, // KeyValueStore を指定。
      code: cloudfront.FunctionCode.fromFile({
        filePath: "./lib/function.js",
      }),
    });

    // CloudFront Distribution
    const distribution = new cloudfront.Distribution(this, "Distribution", {
      defaultBehavior: {
        origin:
          cloudfront_origins.S3BucketOrigin.withOriginAccessControl(bucket),

        // Basic認証を有効にする
        functionAssociations: [
          {
            function: cloudfrontFunction,
            eventType: cloudfront.FunctionEventType.VIEWER_REQUEST,
          },
        ],
      },

      // index.html をデフォルトのオブジェクトとして設定
      defaultRootObject: "index.html",
    });

    // Distribution のドメイン名を出力
    new cdk.CfnOutput(this, "DistributionDomainName", {
      value: distribution.distributionDomainName,
    });

    // S3 バケットへのコンテンツのデプロイ
    new s3_deployment.BucketDeployment(this, "BucketDeploy", {
      sources: [
        s3_deployment.Source.asset("./docs/api"),
        s3_deployment.Source.data("favicon.ico", ""),
      ],
      destinationBucket: bucket,
      distribution,
    });
  }
}

上記を CDK デプロイします。

ベーシック認証のユーザー名とパスワードを KeyValueStore に登録します。

ETAG=$(aws cloudfront-keyvaluestore describe-key-value-store --kvs-arn $KEY_VALUE_STORE_ARN --query 'ETag' --output text)
aws cloudfront-keyvaluestore put-key --key user01 --value hoge --kvs-arn $KEY_VALUE_STORE_ARN --if-match $ETAG

CloudFront のドメイン名をブラウザで開き、ベーシック認証を行うと、Swagger UI が表示されました!

同ページの下半分です。当然ですがブラウザで操作してインタラクティブな API ドキュメントとして利用できます。

Swagger UI にトップバーを表示させる場合

先述の unpkg を使った Swagger UI のドキュメントでは、layoutStandaloneLayout に変更することによりトップバーを表示するサンプルも紹介されていたので試してみます。docs/api/index.html をサンプルを参考に下記のように変更します。

docs/api/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="description" content="SwaggerUI" />
    <title>SwaggerUI</title>
    <link
      rel="stylesheet"
      href="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css"
    />
  </head>
  <body>
    <div id="swagger-ui"></div>
    <script
      src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-bundle.js"
      crossorigin
    ></script>
    <script
      src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-standalone-preset.js"
      crossorigin
    ></script>
    <script>
      window.onload = () => {
        window.ui = SwaggerUIBundle({
          url: "./petstore.yaml",
          dom_id: "#swagger-ui",
          presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
          layout: "StandaloneLayout",
          validatorUrl: null, // バリデーターバッジを非表示にする
        });
      };
    </script>
  </body>
</html>

再度 CDK デプロイを行って同ドメイン名をブラウザで開くと、Explore メニューが付いたトップバーが表示されました。

同ページの下半分です。こちらは先程と同じです。

ちなみに validatorUrl: null としない場合はページ右下にバリデーターバッジが表示されるのですが、なぜか必ずバリデーションエラーが発生してしまいます。

バッジのリンクを踏むと下記の画面に遷移します。yml ファイルが読み込めないため、バリデーションエラーが発生しているようです。

{"schemaValidationMessages":[{"level":"error","message":"Can't read from file https://d1plcl5stempph.cloudfront.net/petstore.yaml"}]}

ただしブラウザから Swagger UI を利用する上では問題はないため、validatorUrl: null としてバリデーターバッジを非表示にするのが良いでしょう。

複数の OAC ドキュメントをホストする場合

Swagger UI では複数の OAC ドキュメントに対応した表示も可能です。追加のサンプル OAC ドキュメントを、docs/api/bookstore.json としてローカルに配置します。

Bookstore API ドキュメント
docs/api/bookstore.json
{
  "openapi": "3.0.0",
  "info": {
    "title": "Book Store API",
    "description": "Simple bookstore management API",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.bookstore.com/v1",
      "description": "Production server"
    }
  ],
  "paths": {
    "/books": {
      "get": {
        "summary": "Get all books",
        "tags": ["books"],
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of books",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Book"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a book",
        "tags": ["books"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Book created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Book"
                }
              }
            }
          }
        }
      }
    },
    "/books/{id}": {
      "get": {
        "summary": "Get book by ID",
        "tags": ["books"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Book details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Book"
                }
              }
            }
          },
          "404": {
            "description": "Book not found"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Book": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "author": {
            "type": "string"
          },
          "price": {
            "type": "number"
          },
          "category": {
            "type": "string"
          }
        }
      },
      "BookInput": {
        "type": "object",
        "required": ["title", "author", "price"],
        "properties": {
          "title": {
            "type": "string"
          },
          "author": {
            "type": "string"
          },
          "price": {
            "type": "number"
          },
          "category": {
            "type": "string"
          }
        }
      }
    }
  }
}

そして docs/api/index.html を下記のように変更します。複数の OAC ドキュメントを読み込むために、urls プロパティを使用しています。

docs/api/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="description" content="SwaggerUI" />
    <title>SwaggerUI</title>
    <link
      rel="stylesheet"
      href="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css"
    />
  </head>
  <body>
    <div id="swagger-ui"></div>
    <script
      src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-bundle.js"
      crossorigin
    ></script>
    <script
      src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-standalone-preset.js"
      crossorigin
    ></script>
    <script>
      window.onload = () => {
        window.ui = SwaggerUIBundle({
          // 複数の OpenAPI ドキュメントを読み込む
          urls: [
            {
              url: "./echo.yaml",
              name: "Echo API",
            },
            {
              url: "./petstore.yaml",
              name: "Petstore API",
            },
          ],
          dom_id: "#swagger-ui",
          presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
          layout: "StandaloneLayout",
          validatorUrl: null, // バリデーションを無効化
        });
      };
    </script>
  </body>
</html>

再度 CDK デプロイを行ってドメイン名をブラウザで開くと、トップバーの右上に Select a definition ドロップダウンが表示されるようになりました。

ドロップダウンリストを操作すると、選択した OAC ドキュメントの API に表示を切り替えることができます。


おわりに

OpenAPI ドキュメントを CloudFront + S3 上でベーシック認証付きでホスティングして Swagger UI で視覚化する構成を AWS CDK で実装してみたので、紹介しました。

今回の構成を使うことで、セキュリティを保ちながら API ドキュメントを公開できます。どなたかの参考になれば幸いです。

参考

https://docs.aws.amazon.com/ja_jp/cli/latest/reference/cloudfront-keyvaluestore/put-key.html

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-kvp.html#work-with-kvs-cli-keys

https://dev.classmethod.jp/articles/aws-cdk-cloudfront-functions-keyvaluestore/

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.