はじめに
皆さん API 仕様書を書くのは好きですか!
私はあまり好きではありません!!!
理由は色々ありますが、大きいところで言うと以下です。
- 記述量が多くシンプルに時間が掛かる
- OpenAPI の記法が独特で書くのがめんどくさい
- API に変更があった際更新漏れが発生しがち
つまり何が言いたいかというと 出来るだけ楽したい!!!
ということで今回のゴールとしては
「.js ファイルに記述した内容が CLI コマンドの実行で Swagger で見れるようになる」
ことを目指します。
この”Swagger で見れるようにする”については、具体的に以下の 2 つの手法を紹介していきます。
- VSCode の拡張機能を使った表示
- ブラウザでの表示
なお、API 仕様書といえば Excel や Notion で作成するものも指しますが、今回の記事では OpenAPI で書く API 仕様書に限って話していきたいと思います。
2 つの開発手法
実装に入る前に API の開発手法について簡単に紹介しておきます。
API を実装する際の開発手法には主に 2 つのアプローチがあります。
- デザインファースト(トップダウン型): OpenAPI からコードを生成する手法
-
コードファースト(ボトムアップ型): コードから OpenAPI の仕様を生成する
それぞれの適用ケースについては以下の記事が分かりやすかったので、気になる方は見てみてください。
今回は「楽に API 仕様書を作る」ことが目的のため、コードファースト(ボトムアップ型)の手法を紹介していきます。
技術スタック
- Node.js: 18.20.4
- Express: 4.21.1
- Sequelize: 6.37.5
- swagger-jsdoc: 6.2.8
- swagger-ui-express: 4.6.3
ディレクトリ構成
今回の記事に関連のある部分だけですが、ディレクトリ構成を紹介しておきます。
app
├── swagger
│ ├── swagger.config.js
│ └── schemas
│ ├── Pet.js
│ └── Tag.js
├── docs
│ └── openapi.yml
├── router.js
└── app.js
.js ファイルに OpenAPI 仕様を記述する
まずはいずれの表示方法でも必要になる、.js ファイルへの OpenAPI 仕様の記述について書いていきたいと思います。
swagger-jsdoc のインストール
今回使うライブラリは swagger-jsdoc です。
有名なライブラリなので名前は聞いたことがある方は多いと思います。
このライブラリは JSDoc のアノテーションをされたソースコードを読み込み、OpenAPI 仕様を生成してくれます。
以下のコマンドでインストールを行いましょう。
$ npm install swagger-jsdoc --save
Schema の作成
インストールが完了したら早速.js ファイルに OpenAPI で出力する内容を書いていきます。
Schema については API のファイルに直接書き込んでもいいのですが、大規模開発だと長くなりがちですし、汎用性が低いため今回は別ファイルに書いていきます。
(あとは JSDoc を使っておいてなんですが、コメントでの記入はシンプルに書きにくい。)
今回は Swagger 公式のサンプルを参考に Pet と Tag の Schema を作っていきます。
module.exports = {
type: "object",
properties: {
id: { type: "integer", format: "int64" },
name: { type: "string" },
},
};
module.exports = {
type: "object",
properties: {
id: { type: "integer", format: "int64", example: 10 },
name: { type: "string", example: "doggie" },
category: {
type: "object",
properties: {
id: { type: "integer", format: "int64", example: 1 },
name: { type: "string", example: "Dogs" },
},
},
photoUrls: { type: "array", items: { type: "string" } },
tags: {
type: "array",
items: { $ref: "#/components/schemas/Tag" },
},
},
};
API に仕様を記載
Schema ができたら router.js に API を記述していきます。
ここは@openapi
を挿入する以外は、OpenAPI と同じ構文で書けば問題ないです。
/**
* @openapi
* /pet/{petId}:
* get:
* tags:
* - pet
* summary: Find pet by ID
* description: Returns a single pet
* 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/json:
* schema:
* $ref: '#/components/schemas/Pet'
*/
app.get("/pet/:petId", function (req, res, next) {
/* 省略 */
});
OpenAPI 仕様書の定義ファイルを作成
定義ファイルは以下の通り書いていきます。
app.js に直接記述する人も多いようですが、私は今回別で swagger.config.js というファイルを作成し module.exports する形を取っています。
const swaggerJSDoc = require('swagger-jsdoc');
const path = require("path");
const schemasDir = path.resolve(__dirname, "./schemas");
const options = {
definition: {
openapi: "3.0.0",
info: {
title: "Swagger Petstore - OpenAPI 3.0",
version: "1.0.0",
description: "APIの説明を記入",
},
servers: [
{
url: "https://petstore3.swagger.io/api/v3",
description: "Swagger公式のサンプルのURL",
},
],
tags: [
{
name: 'pet',
description: 'Everything about your Pets',
},
],
components: {
schemas: {
Tag: require(path.join(schemasDir, "Tag")),
Pet: require(path.join(schemasDir, "Pet")),
},
},
},
apis: ["./router.js"], // API 定義のコメントがあるルーターを指定
};
const swaggerSpec = swaggerJSDoc(options);
module.exports = swaggerSpec;
先ほど作った Schema はここで読み込んでいきます。
apis に.ts ファイルを渡すと、エンドポイントが表示されません。
解決策もあるようですが、全ての.ts ファイルをスキャンすることになりロードに時間が掛るようです。
.ts ファイルを使いたい場合は、build 後の.js ファイルを渡すなどの工夫が必要そうです。
VSCode の拡張機能で Swagger を表示する
「ブラウザでプレビューできればいいので.yml ファイルは必要ないよ!」という方は、次の ”ブラウザで Swagger を表示する” の章にスキップして頂いて構いません。
CLI コマンドで OpenAPI 仕様の.yml ファイルを生成する
今回は docs 配下にopenapi.yml
というファイルで出力を行います。
コマンドを以下の通りじっこうしてSwagger specification is ready.
が表示されれば、出力が問題なく出来ています。
$ # npx swagger-jsdoc -d <OpenAPI定義ファイル> <API定義ファイル> -o <出力ファイル>
$ npx swagger-jsdoc -d app/swagger/swagger.config.js app/router.js -o docs/openapi.yml
Swagger specification is ready.
docs 配下に出来ている`openapi.yml`を開くと出力された内容が確認できると思います。
openapi: 3.0.0
info:
title: Swagger Petstore - OpenAPI 3.0
version: 1.0.0
description: APIの説明を記入
servers:
- url: https://petstore3.swagger.io/api/v3
description: 上記はSwaggerのサンプルのurlです。適宜変更してください。
tags:
- name: pet
description: Everything about your Pets
components:
schemas:
Tag:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
Pet:
type: object
properties:
id:
type: integer
format: int64
example: 10
name:
type: string
example: doggie
category:
type: object
properties:
id:
type: integer
format: int64
example: 1
name:
type: string
example: Dogs
photoUrls:
type: array
items:
type: string
tags:
type: array
items:
$ref: "#/components/schemas/Tag"
paths:
"/pet/{petId}":
get:
tags:
- pet
summary: Find pet by ID
description: Returns a single pet
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/json:
schema:
$ref: "#/components/schemas/Pet"
VSCode の拡張機能で表示する
では、出力された.yml ファイルを VSCode の拡張機能を使って表示してみましょう。
今回使う拡張機能はOpenAPI (Swagger) Editorです。
Swagger Viewer という拡張機能もありますが、こちらはコピペが出来ず不便なので、こちらの拡張機能をオススメします。
インストールを行った後は.yml ファイルを開いた状態で、画像のアイコンをクリックすることでプレビュー画面を表示することが出来ます。
以下のようにプレビューが表示されれば完了です。
ブラウザで Swagger を表示する
次はブラウザでの表示を実装していきましょう。
こちらも上記の章同様、不要な方はスキップして頂いて構いません。
swagger-ui-express のインストール
ライブラリは swagger-ui-express を使っていきます。
以下のコマンドでインストールを行いましょう。
$ npm install swagger-ui-express
バージョンに注意してください。
バージョンが新しすぎた場合、うまく動作しないケースがありました。
うまくいかない場合は本記事と同じ v4.6.3 で試してみてください。
セットアップ
インストールが完了したら、app.js で swagger-ui-express のセットアップを行っていきます。
セットアップで渡す定義ファイルは、初めに作成した swagger.config.js を使います。
app.js に以下の内容を追加しましょう。
const express = require('express');
const cors = require('cors');
const swaggerUi = require('swagger-ui-express');
const swaggerSpec = require('./swagger/swagger.config');
const app = express();
app.use(cors());
// http://localhost:3000/api-docs/に表示
app.use('/api-docs', swaggerUi.serveWithOptions({ redirect: false }));
app.get('/api-docs', swaggerUi.setup(swaggerSpec));
今回私のプロジェクトでは API をhttp://localhost:3000
に立てているため、上記の記載の場合、ブラウザでのプレビューはその配下の/api-docs/
に表示されるようになります。
公式の README とは違う実装をしています。
12 行目の部分は公式の README ではswaggerUi.serve
と記述されていますが、私の場合これではうまく表示が出来ませんでした。
そのため以下の記事を参考にswaggerUi.serveWithOptions({ redirect: false })
を採用しています。
https://github.com/scottie1984/swagger-ui-express/issues/62
CLI コマンドでブラウザにプレビューを表示する
セットアップが完了したら、CLI では app.js を呼び出すだけです。
以下の通り node で呼び出しを行います。
node app/app.js
実行が完了したら、設定した URL にアクセスしてみましょう。
以下の画像の通り、プレビューが表示されれば完了です。
最後の'/'は必須です。
http://localhost:3000/api-docs
でアクセスすると以下の様なエラーが出て、表示ができないため注意しましょう。
Refused to apply style from 'http://localhost:3000/swagger-ui.css' because its MIME type ('application/json') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
オマケ: Authentication の設定
本記事の内容は以上で終了にはなるのですが、Swagger で Authentication を利用している方は多いと思いましたので、オマケとして swagger-jsdoc を使っている際の書き方について簡単に触れておきたいと思います。
Authentication の内容については、OpenAPI の定義ファイルに記載する必要があります。
例として今回の swagger.config.js に公式サイトに記載されている X-API-KEY の内容を追記してみるとこんな感じです。
const swaggerJSDoc = require('swagger-jsdoc');
const path = require("path");
const schemasDir = path.resolve(__dirname, "./schemas");
const options = {
definition: {
openapi: "3.0.0",
info: {
title: "Swagger Petstore - OpenAPI 3.0",
version: "1.0.0",
description: "APIの説明を記入",
},
servers: [
{
url: "https://petstore3.swagger.io/api/v3",
description: "Swagger公式のサンプルのURL",
},
],
tags: [
{
name: 'pet',
description: 'Everything about your Pets',
},
],
components: {
// ----- この範囲を追加 -----
securityDefinitions: {
APIKeyHeader: {
type: 'apiKey'
in: 'header'
name: 'X-API-Key'
},
},
// ------------------------
schemas: {
Tag: require(path.join(schemasDir, "Tag")),
Pet: require(path.join(schemasDir, "Pet")),
},
},
},
apis: ["./router.js"],
};
const swaggerSpec = swaggerJSDoc(options);
module.exports = swaggerSpec;
それぞれの API への記載は OpenAPI 仕様で同様に書けばいいので、以下のようになります。
/**
* @openapi
* /pet/{petId}:
* get:
* tags:
* - pet
* summary: Find pet by ID
* description: Returns a single pet
* 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/json:
* schema:
* $ref: '#/components/schemas/Pet'
* security:
* - APIKeyHeader: []
*/
app.get("/pet/:petId", function (req, res, next) {
/* 省略 */
});
最後に
今回は swagger-jsdoc を使った OpenAPI 仕様書の出力と、swagger-ui-express を使ったブラウザ出力を書いてきました。
なお、筆者の「出来るだけ楽して API 仕様書を作りたい」という欲求は継続しています。
具体的には router ファイルに書いているコメントについては、結局 OpenAPI 仕様で書いているので、正直書きにくさは感じています。
正直定義ファイルだけ書いて、あとはコードの内容をもとに自動生成してほしい...。
そのため、読んでくださった皆さんの方で「こうした方がもっと楽になるよ!」や「このライブラリがオススメだよ!」などありましたら、是非コメントなどで教えていただけるとありがたいです!
この記事で皆さんの API 仕様書作りが少しでも楽になれば幸いです。