爆速構築！json-serverでMock API

 
# はじめに

フロントエンド開発のためにAPIのモックが必要になったので、**json-server**を利用してモックサーバを構築していきます。

# 基本的な環境構築

## 初期化

```powershell:powershell
# プロジェクトディレクトリを作成し, 移動する
mkdir mock-api
cd ./mock-api

# 初期化処理
npm init -y
```

## json-server

今回のメインとなる **json-server** を導入します。

```powershell:powershell
npm i -D json-server
```

## contents.json

エンドポイントとなる json ファイルを作成します。

```powershell:powershell
mkdir api
new-item api/contents.json
```

作成した **contents.json** を編集します。
ここで編集した内容がAPIのレスポンスとして返されるようになります。

```json:contents.json
{
    "contents": [
        {
            "id": 1,
            "title": "title - foo",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 2,
            "title": "title - foo2",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 3,
            "title": "title - bar",
            "body": "body - bal",
            "author-id": 2
        }
    ]
}
```

## package.json

**package.json** を更新します。

```json:package.json
{
  "name": "mock-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    ### ↓↓↓↓↓ ここから追加 ↓↓↓↓↓ ###
    "json-server": "json-server --watch ./api/contents.json --port 5000"
    ### ↑↑↑↑↑ ここまで追加 ↑↑↑↑↑ ###
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.16.1"
  }
}
```

## 実行

**`npm run`** コマンドで実行します。

```powershell:powershell
npm run json-server
```

実行されると、以下に**GET通信**することで先ほどの**contents.json**の内容が得られます。

```
http://localhost:5000/contents
```

# 応用的な環境構築

## エンドポイントを複数用意したい場合

エンドポイントを複数用意したい場合、**contents.json**にもう一つ要素を追加することで解決します。

```json:contents.json
{
    "contents": [
        {
            "id": 1,
            "title": "title - foo",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 2,
            "title": "title - foo2",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 3,
            "title": "title - bar",
            "body": "body - bal",
            "author-id": 2
        }
    ],
    "authors": [
        {
            "id": 1,
            "name": "hoge"
        },
        {
            "id": 2,
            "name": "fuga"
        }
    ]
}
```

上記のように編集し実行すると、以下のように通信先を切り替えることができます。

```
http://localhost:5000/contents
http://localhost:5000/authors
```


### エンドポイント毎にファイルを分割したい場合

しかし、APIの規模が大きくなってくると単一ファイルではメンテナンスが難しくなってくることは想像に難しくありません。
そうなると「エンドポイント毎にファイルを分割したい」という欲求が生まれてきます。

そこで問題となってくるのが **json-server** の「単一ファイルしか受け付けない」という仕様です。
なので、今回は複数ファイルを一つにマージすることで対応したいと思います。

> 【参考】
>　　- [Json-serverでモックAPI](https://qiita.com/haneri/items/5930c579b0c803b364e9)

(1) まず、マージをするためのスクリプトを用意します

```powershell:powershell
mkdir scripts
new-item merge.js
```

```javascript:scripts/merge.js
const path = require("path");
const fs = require("fs");

const root = path.resolve("./", "api");
const update = () => 
{
    const api = fs.readdirSync(root).reduce((api, file) => {
      if (api === undefined)
        api = {};
    
      if (path.extname(file) == ".json") {
        const endpoint = path.basename(file, path.extname(file));
    
        if (api[endpoint] === undefined)
          api[endpoint] = {};
    
        api[endpoint] = JSON.parse(fs.readFileSync(root + "/" + file, "utf-8"));
        return api;
      }
    }, {});
    
    fs.writeFile(root + "/../merged.json", JSON.stringify(api), err => {
      if (err)
          throw err;
    });
}

// 初回作成
update();

// jsonファイルを監視し, 監視ファイルに更新があるたびmerged.jsonを更新
fs.watch(root, (e, filename) => update());
```

(2) **json-server** と **merge.js** を両方同時に動作させるために **npm-run-all** を導入します

```powershell:powershell
npm i -D npm-run-all
```

(3) **package.json** を更新します

```json:package.json
{
  "name": "mock-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    ### ↓↓↓↓↓ ここから追加・更新 ↓↓↓↓↓ ###
    "json-server": "json-server --watch merged.json --port 5000",
    "merge": "node ./scripts/merge.js",
    "serve": "npm-run-all -p merge json-server"
    ### ↑↑↑↑↑ ここまで追加・更新 ↑↑↑↑↑ ###
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.16.1"
  }
}
```

(4) **contents.json** を **contents.json** と **authors.json** に分割します

```powershell:powershell
new-item api/authors.json
```

```json:contents.json
{
    "contents": [
        {
            "id": 1,
            "title": "title - foo",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 2,
            "title": "title - foo2",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 3,
            "title": "title - bar",
            "body": "body - bal",
            "author-id": 2
        }
    ]
}
```

```json:authors.json
{
    "authors": [
        {
            "id": 1,
            "name": "hoge"
        },
        {
            "id": 2,
            "name": "fuga"
        }
    ]
}
```

(5) 以下のコマンドで実行します

```powershell:powershell
npm run serve
```

これで複数ファイルに分割定義することが可能となります。



## POSTやPUTでもレスポンスを受け取りたい場合

**json-server** は **GET以外** のリクエストだと思ったようにレスポンスを返してくれないので、処理をフックして **GET通信** に偽装します。

> 【参考】
>　　- [json-server で使い捨てモックサーバを作る](https://qiita.com/mykysyk@github/items/a2c3bbe63955bbe194d7)
>　　- [JSON Serverを使ってGETとPOSTでレスポンスを変えてみた](https://qiita.com/unpi/items/4f5951af615db7d19cee)


(1) まず、処理をフックするためのスクリプトを追加します

```powershell:powershell
new-item ./scripts/middleware.js
```

```javascript:middleware.js
module.exports = (req, res, next) =>
{
    if(req.method == 'POST') {
        req.method = 'GET'      // GETに偽装
        req.query = req.body
    }
    next()
}
```

(2) **package.json** を更新します

以下では、**--middlewares** オプションを追加しています。

```json:package.json
{
  "name": "mock-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    ### ↓↓↓↓↓ ここから更新 ↓↓↓↓↓ ###
    "json-server": "json-server --watch merged.json --port 5000 --middlewares ./scripts/middleware.js",
    ### ↑↑↑↑↑ ここまで更新 ↑↑↑↑↑ ###
    "merge": "node ./scripts/merge.js",
    "serve": "npm-run-all -p merge json-server"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.16.1"
  }
}
```

これで**POST通信**でもjsonレスポンスを得ることができます。



## APIのrouteをカスタムしたい場合

現状、APIのrouteが **http://localhost:5000/{jsonファイル名}** となっているため、これを **http://localhost:5000/api/v1/{jsonファイル名}** となるように変更してみたいと思います。

> 【参考】
>　　- [Json-serverでモックAPI](https://qiita.com/haneri/items/5930c579b0c803b364e9)

(1) まず、**routes.json** を追加します

```powershell:powershell
new-item ./routes.json
```

```json:routes.json
{
  "/api/v1/*": "/$1"
}
```


(2) **package.json** を更新します

以下では、**--routes** オプションを追加しています。

```json:package.json
{
  "name": "mock-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    ### ↓↓↓↓↓ ここから更新 ↓↓↓↓↓ ###
    "json-server": "json-server --watch merged.json --port 5000 --routes ./routes.json --middlewares ./scripts/middleware.js",
    ### ↑↑↑↑↑ ここまで更新 ↑↑↑↑↑ ###
    "merge": "node ./scripts/merge.js",
    "serve": "npm-run-all -p merge json-server"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.16.1"
  }
}
```

これで**http://localhost:5000/api/v1/{jsonファイル名}**でアクセスできるようになります。


# おわりに

さらに詳しくは **[公式サイト](https://github.com/typicode/json-server)** をご参照ください。