2026年7月28日

2026年7月28日

VPSにOpenAPI対応のAPIサーバーを構築する方法|Node.js・Fastify・Swagger UI・Nginx

OpenAPIは、REST APIのエンドポイント、パラメーター、レスポンスの形を記述する仕様です(以前はSwagger仕様と呼ばれていました)。OpenAPIの仕様書があれば、ブラウザで読めるAPIドキュメント(Swagger UI)を表示したり、クライアントのコードを生成したりできます。

この記事では、VPS上に「OpenAPIの仕様書を自動で出力するAPIサーバー」を作ります。Node.jsのWebフレームワーク Fastify と、公式プラグインの @fastify/swagger(仕様書の生成)と @fastify/swagger-ui(ドキュメント画面)を使います。ルートに書いたスキーマから仕様書が作られるので、実装とドキュメントがずれにくい構成です。

想定する環境は2026年10月時点の次のとおりです。

  • OS: Ubuntu 24.04 LTS
  • Node.js: 24系(LTS)
  • Fastify 5系、@fastify/swagger 9系、@fastify/swagger-ui 5系以上
  • Webサーバー: Nginx(リバースプロキシとHTTPS)

1. Node.jsをインストールする

Fastify 5はNode.js 20以上が必要です。Ubuntu 24.04の標準リポジトリのNode.jsは18系で要件を満たさないため、NodeSourceのリポジトリから入れます。

sudo apt update
sudo apt install -y curl
curl -fsSL https://deb.nodesource.com/setup_24.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt install -y nodejs

node -v
npm -v

NodeSourceの nodejs パッケージには npm も含まれています。Ubuntuの npm パッケージを別に入れると依存関係が衝突するので、apt install npm は実行しません。

2. APIサーバーを作る

アプリケーション用のディレクトリを作り、パッケージを入れます。

sudo mkdir -p /opt/openapi-server
sudo chown $USER:$USER /opt/openapi-server
cd /opt/openapi-server

npm init -y
npm install fastify @fastify/swagger @fastify/swagger-ui

/opt/openapi-server/server.js を次の内容で作ります。

'use strict'

const fastify = require('fastify')({ logger: true })

async function main () {
  // OpenAPI ドキュメントの生成(ルート定義より前に登録する)
  await fastify.register(require('@fastify/swagger'), {
    openapi: {
      openapi: '3.0.3',
      info: {
        title: 'Sample API',
        version: '1.0.0'
      }
    }
  })

  // Swagger UI を /docs で表示する
  await fastify.register(require('@fastify/swagger-ui'), {
    routePrefix: '/docs'
  })

  fastify.get('/hello', {
    schema: {
      description: 'あいさつを返す',
      querystring: {
        type: 'object',
        properties: {
          name: { type: 'string', maxLength: 50 }
        }
      },
      response: {
        200: {
          type: 'object',
          properties: {
            message: { type: 'string' }
          }
        }
      }
    }
  }, async (request) => {
    const name = request.query.name || 'World'
    return { message: `Hello ${name}!` }
  })

  const port = Number(process.env.PORT || 3000)
  const host = process.env.HOST || '127.0.0.1'
  await fastify.listen({ port, host })
}

main().catch((err) => {
  fastify.log.error(err)
  process.exit(1)
})

ポイントは次のとおりです。

  • @fastify/swagger は、ルートより前に await を付けて登録する。後から登録したルートは仕様書に載らない
  • 各ルートの schema が、そのまま仕様書のパラメーターとレスポンスになる。querystring のスキーマは入力の検証にも使われ、条件に合わないリクエストには400が返る
  • host を 127.0.0.1 にして、外からは直接つながらないようにする(外向きはNginxが受ける)

動作を確かめます。

node server.js

別の端末から次を実行します。

curl "http://127.0.0.1:3000/hello?name=Taro"
# {"message":"Hello Taro!"}

# OpenAPI の仕様書(JSON と YAML)
curl http://127.0.0.1:3000/docs/json
curl http://127.0.0.1:3000/docs/yaml

/docs/json に "openapi":"3.0.3" と /hello の定義が出ていれば成功です。仕様書をファイルとして配りたい場合は curl -s http://127.0.0.1:3000/docs/json > openapi.json で保存できます。確認できたら Ctrl+C で止めます。

3. systemdで常駐させる

アプリケーションを動かす専用のユーザーを作り、systemdのサービスとして登録します。

sudo useradd --system --no-create-home --shell /usr/sbin/nologin apiuser

/etc/systemd/system/openapi-server.service を作ります。

[Unit]
Description=OpenAPI sample server (Fastify)
After=network.target

[Service]
Type=simple
User=apiuser
WorkingDirectory=/opt/openapi-server
Environment=NODE_ENV=production
Environment=HOST=127.0.0.1
Environment=PORT=3000
ExecStart=/usr/bin/node server.js
Restart=on-failure

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now openapi-server
sudo systemctl status openapi-server

# ログの確認
sudo journalctl -u openapi-server -f

apiuser はファイルを読むだけなので、/opt/openapi-server の所有者は作業ユーザーのままでかまいません。コードを更新したら sudo systemctl restart openapi-server で反映します。

4. Nginxでリバースプロキシする

Nginxを入れ、外からのリクエストを 127.0.0.1:3000 に転送します。APIドキュメント(/docs)は誰でも見られる必要がなければ、Basic認証をかけておきます。

sudo apt install -y nginx apache2-utils

# ドキュメント閲覧用のユーザーを作る(パスワードを聞かれる)
sudo htpasswd -c /etc/nginx/.htpasswd-docs docsuser

/etc/nginx/sites-available/openapi-server を作ります。api.example.com は自分のドメインに置き換えます。

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /docs {
        auth_basic "API docs";
        auth_basic_user_file /etc/nginx/.htpasswd-docs;
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
sudo ln -s /etc/nginx/sites-available/openapi-server /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

ブラウザで http://api.example.com/docs を開き、Basic認証のあとにSwagger UIが表示されれば完成です。画面の「Try it out」から /hello を実際に呼び出せます。

5. ファイアウォールとHTTPS

ufwを使う場合は、SSHとHTTP/HTTPSだけを開けます。Node.jsの3000番は 127.0.0.1 で待ち受けているので、開ける必要はありません。

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status

ドメインのDNSがVPSを向いていれば、certbotでLet's Encryptの証明書を取得してHTTPSにできます。

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d api.example.com

Basic認証のパスワードはHTTPSでないと平文で流れるので、ドキュメントを外に出すならHTTPS化までを1セットで行ってください。

運用での注意

  • APIの認証: この例の /hello は誰でも呼べます。データを返すAPIには、APIキーやトークンによる認証をアプリケーション側で実装する
  • ドキュメントの公開範囲: Swagger UIはエンドポイントの一覧そのものなので、社内向けのAPIなら上の例のように認証をかけるか、/docs をNginxで外から見えないようにする
  • 依存パッケージの更新: npm outdated で古いパッケージを確認し、npm audit で既知の脆弱性を確認する。Fastifyのメジャーバージョンを上げるときは、プラグインの対応バージョンも合わせる(@fastify/swagger 9系と@fastify/swagger-ui 5系以上がFastify 5系に対応)
  • Node.jsのサポート期限: Node.jsは偶数版がLTSになり、期限が切れる前に次のLTSへ上げる。2026年10月時点では24系と26系がLTS

まとめ

  • Node.jsはNodeSourceから24系(LTS)を入れる。Ubuntu標準の18系はFastify 5の要件を満たさない
  • Fastifyに @fastify/swagger をルートより先に登録すると、ルートのスキーマからOpenAPIの仕様書が作られる
  • @fastify/swagger-ui で /docs にSwagger UI、/docs/json と /docs/yaml に仕様書が出る
  • アプリは 127.0.0.1 で待ち受けてsystemdで常駐させ、外向きはNginxで受けてHTTPS化する
  • ドキュメントとAPIには、用途に合わせて認証をかける

関連記事:

お気軽にご相談ください

お見積りへ お問い合わせへ
▲