検索やページネーションに対応したモックAPIサーバをサクッと作る

現在携わっているプロジェクトではiOSアプリを開発しているのですが、バックエンドのAPIも同時並行で開発しているため、アプリから叩けるAPIがまだありません。

そこで、**30秒でREST APIが作成できる**が謳い文句の[json-server](https://github.com/typicode/json-server)というライブラリを使用して、ローカルで動くモックAPIサーバを作って開発をしています。

めちゃめちゃ簡単にAPIが作れる上に、何のコードも書かずに全文検索やページネーション、ソートなどの機能も使えるため、非常に重宝しています。

## 必要な環境
- Mac
- Node.js

## サンプルプロジェクト
以下で説明している内容をまとめたサンプルプロジェクトをGithubに公開しています。
実際の開発で使用しているものとほとんど同じものです。
https://github.com/takehilo/json-server-sample

## REST APIサーバを作る
json-serverはデータファイルとしてのJSONファイルが1つあれば以下のコマンドを実行するだけでREST APIサーバが起動します。

```
$ json-server --watch db.json
```

しかし、データファイルの管理やAPIのTLS化などを考えると、CLIとしてではなくモジュールとして使用したほうがカスタマイズ性も高く便利です。

私は以下のようなスクリプトを作成し、リソースごとにデータファイルを分け、またTLS化を行っています。


```server.js
const os = require('os')
const path = require('path')
const fs = require('fs')
const https = require('https')
const jsonServer = require('json-server')
const app = jsonServer.create()
const middlewares = jsonServer.defaults()

const products = require('./data/products.json')
const users = require('./data/users.json')
const routes = {
  products,
  users
}
const router = jsonServer.router(routes)

app.use(middlewares)
app.use(router)

const certFile = fileName => {
  const file = path.join(__dirname, 'certs', fileName)
  return fs.readFileSync(file)
}

const startServer = () => {
  const options = {
    key: certFile('server.key'),
    cert: certFile('server.crt')
  }
  const server = https.createServer(options, app);

  const port = process.env.PORT || 3000
  server.listen(port, () => {
    console.log(`JSON Server is running on https://localhost:${port}`)
  })
}

startServer()
```

`server.js`と同じディレクトリに`data`と`certs`ディレクトリがあり、`data`配下にはデータファイル、`certs`配下にはTLS証明書と秘密鍵が置いてあるイメージです。

スクリプトを実行すると、`https://localhost:3000`でREST APIサーバにアクセスできます。

```
$ node server.js
JSON Server is running on https://localhost:3000
```

## ダミーデータを作成する
ダミーデータの作成には[Faker](https://github.com/Marak/faker.js)というライブラリを使用しています。
READMEを見るとわかりますが、人名から住所、会社名など様々な形式のデータをランダムに生成してくれる便利なライブラリです。
生成されるデータは英語だけでなく、様々な言語に対応していて、単語数は少ないですが日本語もあります。

私は以下のようなスクリプトを作成し、モックAPIが返すデータを生成しています。

```fake.js
const faker = require('faker')
faker.locale = 'ja'

const randomNum = (max) => {
  return Math.floor(Math.random() * (max + 1))
}

const randomBoolean = () => {
  return Math.floor(Math.random() * 2) > 0 ? true : false
}

const createProduct = (id) => ({
  id: id,
  name: faker.commerce.productName(),
  manufacturer: faker.company.companyName(),
  price: randomNum(2000),
  is_favorite: randomBoolean(),
  tags: [1, 2, 3].map(() => faker.random.word())
})

const nums = []
for (let i = 1; i <= 200; i++) {
  nums.push(i)
}

const shops = nums.map(n => createProduct(n))
console.log(JSON.stringify(shops, null, 4));
```

このスクリプトを以下のように実行し、データファイルを作成します。

```
$ node fake.js > data/products.json
```

## オートリロードさせる
データの一部だけ手動で書き換えてアプリの動作を確かめたいとき、データファイルの変更を検知して自動でサーバをリロードしてくれると便利です。
例えば[nodemon](https://github.com/remy/nodemon)を使えばそれが可能です。

```
$ nodemon server.js
```

nodemonはデフォルトでJSONファイルの変更を検知してくれるので、これだけでモックAPIサーバのオートリロードが実現できます。

## やろうと思えば色々カスタマイズできる
json-serverは[Express](https://github.com/expressjs/express)で作られているので、Expressの知識があれば色々カスタマイズできます。

例えば`/products`にGETしたらHTTP403ステータスコードを返すようにしたければ、以下のようにルーティングの実装をすれば良いです。

```(一部抜粋)server.js
const router = jsonServer.router(routes)

app.use(middlewares)

app.get('/areas', (req, res) => {
  res.status(403).json([])
})

app.use(router)
```

## まとめ
json-serverそのものの紹介というより、それをカスタマイズして使う話になってしまいましたが、タイトルにあるようにjson-serverはデフォルトで全文検索やページネーションなどに対応しており、また記事中ではGETにしかふれてませんが、POST/GET/PUT/DELETE/PATCHにも対応していて実際にデータの追加・削除もできるようになっています。

とても便利なライブラリなので、是非利用してみてください。