目次

【実践Tips】Node.jsで"レスポンス切替型"モックAPIを超シンプルに作る方法

はじめに

API連携のテストやフロントエンド開発で「異常系レスポンスも手軽に試したい」「POSTMANやPRISMだとちょっと重い…」と感じたことはありませんか?

そんな時に便利なのが「ファイルでレスポンス内容を切り替えられるモックAPIサーバー」です。Node.js+Expressだけで、たった1ファイルで実装でき、curlや自動テストからも柔軟に制御できます。

どんなことができる?

サンプル実装(server.js全文)

const express = require('express');
const cors = require('cors');
const fs = require('fs');
const path = require('path');

const app = express();
const PORT = 3000;

// 現在のレスポンスファイル名(デフォルト)
let currentResponseFile = 'normal.json';
const responseDir = path.join(__dirname, 'responses');

// ミドルウェア
app.use(cors());
app.use(express.json());

// リクエストログ
app.use((req, res, next) => {
    console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
    console.log('Request Body:', JSON.stringify(req.body, null, 2));
    next();
});

// レスポンス切替API
app.get('/setResponse/:filename', (req, res) => {
    const filename = req.params.filename;
    const filePath = path.join(responseDir, filename);
    let fileList = [];
    try {
        fileList = fs.readdirSync(responseDir).filter(f => f.endsWith('.json'));
    } catch (e) {
        fileList = [];
    }
    if (!fs.existsSync(filePath)) {
        return res.status(400).json({ 
            result: 'NG', 
            message: `ファイルが存在しません: ${filename}`, 
            availableFiles: fileList 
        });
    }
    currentResponseFile = filename;
    res.json({ 
        result: 'OK', 
        message: `次回以降のレスポンスを ${filename} に設定しました。`, 
        availableFiles: fileList 
    });
});

// ファイルからレスポンスを読み込む
function loadMockResponse(filename) {
    try {
        const filePath = path.join(responseDir, filename);
        if (!fs.existsSync(filePath)) return null;
        const data = fs.readFileSync(filePath, 'utf8');
        return JSON.parse(data);
    } catch (e) {
        console.error('レスポンスファイル読込エラー:', e);
        return null;
    }
}

// メインAPI(例:POST)
app.post('/api/users', (req, res) => {
    const { username, password } = req.body;

    // 認証チェックもファイルで切替可能
    if (!username || !password) {
        const authError = loadMockResponse(currentResponseFile);
        if (authError && authError.body) {
            return res.status(authError.httpStatus || 200).json(authError.body);
        }
        return res.json({
            resultCode: "9",
            resultMessage: "認証情報が不正です"
        });
    }

    // メインレスポンス
    const mockResponse = loadMockResponse(currentResponseFile);
    if (mockResponse && mockResponse.body) {
        console.log('Response:', JSON.stringify(mockResponse.body, null, 2));
        return res.status(mockResponse.httpStatus || 200).json(mockResponse.body);
    }

    // デフォルト or フォールバック
    const defaultNormal = loadMockResponse('normal.json');
    if (defaultNormal && defaultNormal.body) {
        return res.status(defaultNormal.httpStatus || 200).json(defaultNormal.body);
    }
    res.json({ resultCode: "0", resultMessage: "", userList: [] });
});

// ヘルスチェック
app.get('/health', (req, res) => {
    res.json({ status: 'OK', timestamp: new Date().toISOString() });
});

// サーバー起動
app.listen(PORT, () => {
    console.log(`🚀 モックサーバー起動: http://localhost:${PORT}`);
});

レスポンスファイル例

* responses/normal.json(正常系)

{
    "httpStatus": 200,
    "body": {
        "resultCode": "0",
        "resultMessage": "正常終了",
        "userList": [
            {
                "id": "001",
                "name": "サンプルデータ1",
                "status": "active"
            },
            {
                "id": "002", 
                "name": "サンプルデータ2",
                "status": "inactive"
            }
        ]
    }
}

* responses/error500.json(エラー系)

{
    "httpStatus": 500,
    "body": {
        "resultCode": "5",
        "resultMessage": "内部サーバーエラーが発生しました",
        "errorDetails": "データベース接続に失敗しました"
    }
}

* responses/auth_error.json(認証エラー)

{
    "httpStatus": 401,
    "body": {
        "resultCode": "9",
        "resultMessage": "認証に失敗しました",
        "errorCode": "AUTH_FAILED"
    }
}

使い方

* 1. プロジェクトセットアップ

# プロジェクトディレクトリ作成
mkdir mock-api-server
cd mock-api-server

# package.json初期化
npm init -y

# 必要なパッケージインストール
npm install express cors

# レスポンスファイル用ディレクトリ作成
mkdir responses

* 2. サーバー起動

node server.js

* 3. レスポンス切替

# 正常系レスポンスに切替
curl http://localhost:3000/setResponse/normal.json | jq .

# エラー系レスポンスに切替
curl http://localhost:3000/setResponse/error500.json | jq .

# 認証エラーに切替
curl http://localhost:3000/setResponse/auth_error.json | jq .

* 4. メインAPIの呼び出し

# POSTリクエストでAPIテスト
curl -X POST http://localhost:3000/api/users \
  -H "Content-Type: application/json" \
  -d '{"username": "testuser", "password": "password123"}' | jq .

* 5. 利用可能なファイル一覧確認

# 存在しないファイルを指定すると、利用可能ファイル一覧が返る
curl http://localhost:3000/setResponse/dummy.json | jq .

どんな時に便利?

* 異常系・境界値テストをすぐ試したいとき

従来のモックツールでは設定が面倒だった異常系レスポンスも、JSONファイルを作成してcurl一発で切り替えられます。

* フロントエンドや結合テストでAPIの返却値を柔軟に変えたいとき

UI開発中に「エラー画面の表示確認をしたい」「データが空の場合の動作を見たい」といったニーズに即座に対応できます。

* OpenAPI仕様が未確定またはPRISM/POSTMANだと重い・運用が面倒なとき

仕様策定中のプロジェクトでも、必要最小限の実装で素早くモック環境を構築できます。

* CI/CDの自動テストでレスポンスパターンをプログラムから切り替えたいとき

テストスクリプト内でレスポンス切替APIを呼び出すことで、様々なシナリオのテストを自動化できます。

さらなる活用Tips

* パターン別ディレクトリ管理

responses/
├── normal/
│   ├── success.json
│   └── empty_list.json
├── error/
│   ├── 400_bad_request.json
│   ├── 401_unauthorized.json
│   ├── 500_server_error.json
│   └── timeout.json
└── edge_case/
    ├── large_data.json
    └── special_characters.json

* 環境変数での設定

const PORT = process.env.PORT || 3000;
const RESPONSE_DIR = process.env.RESPONSE_DIR || path.join(__dirname, 'responses');

* レスポンス遅延の実装

// レスポンスファイルに遅延設定を追加
{
    "httpStatus": 200,
    "delay": 2000,  // 2秒遅延
    "body": { ... }
}

// サーバー側で遅延処理
if (mockResponse.delay) {
    setTimeout(() => {
        res.status(mockResponse.httpStatus || 200).json(mockResponse.body);
    }, mockResponse.delay);
    return;
}

まとめ

Express+ファイル操作だけで、超軽量・高拡張性なモックAPIが作れます。GUI不要・OpenAPI不要・curl一発で切替可能なこのアプローチは、「現場で今すぐ動かしたい」「自動テストを強化したい」プロジェクトに最適です。

シンプルな構成でありながら、実際の開発・テスト現場で求められる柔軟性を兼ね備えているため、チーム開発の生産性向上に大きく貢献できるでしょう。

参考

他にも「こういう機能追加したい」「もっと高度な分岐が欲しい」などあれば、ぜひコメントやフィードバックください!

ご参考になれば幸いです。

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS