22
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

リンクアンドモチベーションAdvent Calendar 2023

Day 7

GitHub 上の OpenAPI Spec を SwaggerUI でURL共有できるツールを作ります🌟

Last updated at Posted at 2023-12-06

こんにちは! リンクアンドモチベーションの川津です🌟

今日のWebサービス開発をしている現場では OpenAPI Specification (旧Swagger) を使って API 仕様を書いてる所は多いかと思います。

弊社でも使ってまして、先日 API 仕様のレビュー依頼が来たのですが、見るまでの手順 ↓ が煩雑で「単に URL 共有してくれると嬉しいのにな〜」と思ったのが本記事の発端です😅

  1. レビュー対象の Branch を pull する
  2. VSCode で Specification ファイル (.yaml / .json) を開く
  3. OpenAPI (Swagger) Editor でプレビュー表示する

完成品

という事でツールを作ってみました。

以下の例では OAI 公式の GitHub で公開されているサンプル petstore.yamlSwaggerUI を用いて描画しています。

GitHub repository:

このツールそのものは認証等のセキュリティ対策は無いので、基本的に利用の際は インターネットに公開してはいけません!
プライベートネットワーク中にホストするか、別途IPアドレス制限等をします。

使い方

URL

URL で GitHub 上の OpenAPI Specification ファイル (.yaml / .json) を指定できます。

https://swagger-github.vercel.app/{owner}/{repo}/{branch}/{path_to_spec}

この URL のパス部分は GitHub Raw ファイル URL raw.githubusercontent.com と同じです。

Pasted_Image_2023_12_04_20_10.png

GitHub (Private Repo) 認証

サーバーサイドの環境変数もしくは .env ファイルで GitHub トークンを指定する様にしました。

# Your Authorization Bearer token. (e.g. GitHub "personal access token")
BEARER_TOKEN=

前述のサンプルで公開しているケースでは BEARER_TOKEN は未指定にしています。

仕組み

要素技術

  • Node.js → 20.10.0
  • Next.js → 単に楽なので使いました (FE/BEまとめて書ける & デプロイ環境)
  • デプロイ先
    • Vercel → 楽だけど、課金しないと IP 制限できなさそう
    • AWS Amplify → CloudFront (WAF) もつければ IP 制限できる

シーケンス

特別な事は何もなく、良く思いつくであろう構成です。

  1. Top-Level Navigation アクセスでは、SwaggerUI 等のシングルページを返す
  2. URLパスを元に、SwaggerUI が OpenAPI Spec ファイル (.yaml / .json) を Ajax で要求
  3. (セキュリティ認証の為に) GitHub へのアクセスをサーバーがプロキシして結果を返す

ページ/API

Next.js なので React でフロントコードを書きます。

SwaggerUI 自体は swagger-ui-react Package を使って数行で描画できます。

'use client'

import dynamic from 'next/dynamic'
const SwaggerUI = dynamic(() => import('swagger-ui-react'), { ssr: false })
import 'swagger-ui-react/swagger-ui.css'

export default function Home({ params }: { params: { path: string[] } }) {
  const githubPath = params.path.join('/')

  return (
    <main className="">
      <SwaggerUI url={`/api/github/${githubPath}`} deepLinking={true} />
    </main>
  )
}

↑ で url={"..."} 指定した URL に Ajax リクエストが来ます。

Next.js の Route Handler で OpenAPI Spec (.yaml / .json) を返す API を用意します。

export async function GET(
  request: Request,
  { params }: { params: { path: string[] } },
) {
  // 認証が必要なら、環境変数から取って来る。
  const token = process.env.BEARER_TOKEN
  const options = token ? {
    headers: { Authorization: `Bearer ${token}` },
  } : undefined

  // raw.githubusercontent.com にアクセス.
  const githubPath = params.path.join('/')
  const url = new URL(githubPath, 'https://raw.githubusercontent.com')
  const res = await fetch(url, options)

  // ...(省略)...

  return new Response(res.blob(), { status: res.status })
}

実際、書いたコードはこれだけです😳

※一部、力技で実装した箇所が...

OpenAPI Specification では $ref 参照を使って、ファイルを分割管理する事ができます。

root.yaml
paths:
  /oauth2/token:
    post:
      $ref: "./child.yml"
  ...
child.yaml
operationId: postOAuth2
tags:
  - OAuth2
summary: アクセストークンを...
requestBody:
  content:
    ...

しかし SwaggerUI を使って表示すると、本来のカテゴリ箇所 (tag) ではなく、全て default カテゴリに表示されてしまいました。

image.png

これはどうやら、子供のファイル child.yaml の方に tags を記述している事が原因のようです。

SwaggerUI はカテゴリ POST /oauth2/token をクリックして開閉するまで、子供のファイル child.yaml を取得しにいかない為、中身がまだ分からないのですね。

GPT に「どうしたらいいの?」と聞くと「サーバーサイドで全部くっつけたらいいよ」とかなり力技の提案をされたので、その通りにしてみました...😅

デプロイ

デプロイ先は次の3パターンを考えました。

重視した観点は ①「なるべく保守をしたくない」、②「インターネットに公開しない」です。

  1. プライベートネットワーク上のサーバー (EC2 とか)
  2. Vercel
  3. AWS Amplify + CloudFront(WAF)

1. プライベートネットワーク上のサーバー (EC2 とか)

ネットワーク上、外部からアクセスできないので正直イチオシの方法です...笑

どこかのサーバー (野良EC2とか) に入って、Next.js サーバーを起動するだけです。

npm run build
npm run start

ブラウザで http://{server-ip-address}:3000 URL にアクセスします。

2. Vercel

Next.js で書いたのなら、Vercel が最も手っ取り早いデプロイオプションでしょう。

画面の指示に従うだけで簡単にできちゃうので、手順は説明しません。

しかしながら、残念な事に「IPアドレス制限」(Trusted IPs) は Enterprise プランでないと使えないようです。

一応アプリケーションコードで弾く方法もありますが、少し怖いので不採用としました。

Pasted_Image_2023_12_04_22_20.png

3. AWS Amplify + CloudFront(WAF)

この方法は正直、初期構築は一番手間なのですが、その後の保守はわりと楽なので社内にデプロイしたのはこの方法を採用しました。

  • Next.js コードは AWS Amplify でサーバレスデプロイできます
  • 更に CloudFront (WAF) を使うことで、IPアドレス制限ができる

CloudFront(WAF) → AWS Amplify 間は Basic 認証はあれど、実際にはインターネット通信なので、唯一そこが心配...

AWS公式様のこの記事 ↓ が大変わかりやすいので、本記事では手順の説明は割愛します。

  1. Amplify を使った Next.js SSR アプリケーションのデプロイ
  2. AWS Amplify がホストするウェブアプリケーションの AWS WAF を有効にする

一つハマったのは AWS Amplify で実行時に「環境変数が適用されない」問題です。
AWS Console (Amplify) で環境変数を設定しても何故か反映されません。

理由は公式のページ ↓ に書いてありました。どうやら実行時の環境変数は .env ファイルで指定する必要があるみたいです。

最終的に AWS Amplify の Build settings amplify.yml はこうなります。

version: 1
frontend:
  phases:
    preBuild:
      commands:
        - nvm use 18
        - node -v
        - npm ci
    build:
      commands:
        - echo "BEARER_TOKEN=$BEARER_TOKEN" >> .env
        - echo "PROXY_DOMAIN=$PROXY_DOMAIN" >> .env
        - echo "RESOLVE_ALL_REFS=$RESOLVE_ALL_REFS" >> .env
        - echo "RESOLVE_ALL_REFS_DEPTH_LIMIT=$RESOLVE_ALL_REFS_DEPTH_LIMIT" >> .env
        - npm run build
  artifacts:
    baseDirectory: .next
    files:
      - '**/*'
  cache:
    paths:

本当は node 20 にしたいけど、現状 Amplify では 18 までしか使えなそう。

最後に

ここまで読んで下さってありがとうございます。
結構ニッチな記事なのですが、どの現場でも一律「これ使えば解決するよね?」という策があまりなさそうだったので、記事化してみました。

この記事が、もし同じ事で悩んでおられる方の役に立てれば幸いです。

22
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?