LoginSignup
0
0

Next.js + Redoc で1秒以内でレスポンスする API 仕様書をつくった

Posted at

こんにちは zane です。

Collections というオープンソースのヘッドレス CMS をつくっています。今回パブリックな API 仕様書が必要になり Next.js(Nextra) + Redoc という組み合わせを試したら、なかなか良かったので手順としてまとめてみました。

OpenAPI(Swagger)などを使った API 仕様書はだいたいどのプロジェクトでも作成しますが、それを公開するとなると、機会はあまり多くはないかもしれません。が、大きく技術トレンドが変わる分野でもないので、覚えておくといずれ役に立つかもしれません!

完成品

api.png

公開されているページはこちら

コードはすべて GitHub で公開していますので、参考にしてみてください。
https://github.com/collectionscms/collections/blob/main/docs/pages/reference/api.ja.mdx

Redoc とは?

OpenAPI 仕様で記述された yaml ファイルから HTML ファイルを生成し、静的なドキュメントにしてくれるツールで、オープンソースとしても公開されています。

Collections は、ドキュメント全体を Nextra (Next.jsベースの静的サイトジェネレーター) で作っていますので、これに統合してレンダリングさせています。

Nextra については、以前記事を書いていますので、参考にしてみてください。
https://zenn.dev/zane/articles/0cf5b21d49125b

React のコンポーネントとして Redoc をインストールする

それでは早速、公式のステップ に沿って進めていきましょう。まずは @redocly/cli と関連する依存関係をインストールします。

npm i react react-dom mobx styled-components core-js

yaml ファイルを準備

次に、OpenAPI 仕様をもとに yaml ファイルを作成します。
Collections の API 仕様書を貼っておきますので、参考してみてください。

VSCode を使っている方は OpenAPI (Swagger) Editor を入れておくと、文法エラーなどワーニングを出してくれるのでサクサク作業が進められます。

コンポーネントに追加する

ファイルの準備ができたらページに Redoc のコンポーネントを埋めると、自動で HTML を生成して出力してくれます!

import { RedocStandalone } from 'redoc';

<RedocStandalone specUrl="url/to/your/spec"/>

ただし specUrlローカルファイルを参照させることはできません。これは GitHub のイシューでもやりとりされていますが、同一オリジンポリシーとセキュリティ上の都合によるものです。
https://github.com/Redocly/redoc/issues/149#issuecomment-359285569

この問題の対応策として S3 などに yaml を退避させる選択肢もありますが、手間は増えるしファイルをアップロードするために CI のジョブを追加したりと、あまりやりたくはありません。。。

Next.js の API で配信し SSG 化する

そのため Next.js のAPIを使って yaml をレスポンスさせ、それを渡すことでこの問題を解決しました。

openapi.ts
export default function handler(_req: NextApiRequest, res: NextApiResponse<ResponseData>) {
  const fullPath = path.join(dataDirectory, 'api.yaml');
  const fileContents = fs.readFileSync(fullPath, 'utf8');
  res.status(200).json(YAML.parse(fileContents));
}

まぁ、これだけでも良かったのですが、都度リクエストが走ることでレンダリングが結構遅い。。。そのため、もう一工夫が必要です。Redoc にはspecという URL の代わりにオブジェクトを渡すプロパティもあるので

export const getStaticProps = ({ params }) => {
  return fetch(`https://collections.dev/api/openapi`)
    .then((res) => res.json())
    .then((repo) => ({
      props: {
        ssg: repo,
      },
    }));
};

export const Redoc = () => {
  const data = useData();
  return <RedocStandalone spec={data} />;
};

このように SSG で事前レンダリングしてオブジェクトを渡すことで、見事レスポンス面もクリアできました!

パフォーマンス

$ curl 'https://collections.dev/ja/reference/api/' -H 'Accept-Encoding: gzip, deflate, sdch' -H 'Accept-Language: en-US,en;q=0.8,ja;q=0.6' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.86 Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' -H 'Connection: keep-alive' --compressed -o /dev/null -w  "%{time_starttransfer}\n" -s

0.239587
0.249859
0.255718
0.249946
0.253883

と、だいたい 300ms 以内でレスポンスされます。

speed

First Contentful Paint も1秒くらい。これ以上改善しても SEO が求められるページでもないので、合格点を出してもいいのではないでしょうか!

最後に

ドラッグ&ドロップで WordPress を API 化するオープンソースのヘッドレス CMS 【 Collections 】をつくっています!

Markdown(with GFM) やダークモードなど、書き心地の良さも持ち味です。 Live Demo も公開していますので、ぜひ気軽に触ってみてください 🙌🙌

それでは

0
0
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
0
0