jsonファイルを用意するだけでAPIとして機能させることが出来る
mock用のREST APIを簡単に立ち上げることが出来ます
フロントエンド開発時やアプリ開発時にバックエンドの実装を待たずに開発に入ったりなんて事ができそうですね
実際にいじってみる
インストール
npm install -g json-server
これだけ、簡単ですね
基本
データベースに見立ててくれるjsonファイルを記述します
heroes
,nest
はそれぞれ apiのパスに相当します
http://localhost:3000/heroesで対象のリソースにアクセスできるようになります
- db.json
{ "heroes": [ { "id": "aaa", "name": "HelloMan" }, { "id": "ccc", "name": "SaibaRyo333" }, { "id": "bbb", "name": "Ultraman" } ], "nest": [ { "id": 1, "name": "test", "cond": { "age": 30, "prefecture": "kanagawa" } }, { "id": 2, "name": "sandbox", "cond": { "age": 31, "prefecture": "tokyo" } } ] }
- mock起動
json-server --watch db.json -p 3003
今回は3003番ポートで起動した
- get
curl 'http://localhost:3003/heroes' [ { "id": "aaa", "name": "HelloMan" }, { "id": "ccc", "name": "SaibaRyo333" }, { "id": "bbb", "name": "Ultraman" } ]
対象のリソースから全件取得した
- get(id指定)
curl 'http://localhost:3003/heroes/aaa' { "id": "aaa", "name": "HelloMan" }
- post
curl -XPOST -H "Content-Type: application/json" 'http://localhost:3003/heroes' -d '{"id":"ddd","name":"PostMan"}' { "id": "ddd", "name": "PostMan" }
db.jsonの中身を確認してみる
- db.json
}, { "id": "ddd", "name": "PostMan" }
ちゃんと反映されている
routes
json-serverの制約としてリソースパスにスラッシュを含めることが出来ない
具体的に言うとaccounts/campaigns
のようなパスが設定出来ない
モックで作りたい場合はパスを書き換えて1階層で完結できるように対応する必要がある
- db.json
{ "putjob": [ {"id":"798314638660210688"} ], "job": [ {"url": "http://test.com", "id_str": "798314638660210688", "status": "PROCESSING"}, {"url": "http://test.com", "id_str": "798314638660210689", "status": "PROCESSING"} ] }
- routes.json
{ "/jobs/accounts/:account_id": "/jobs" }
左辺のパスを右辺に変換する
なので今回の場合だと/jobs/accounts/:account_id
をjson-server側では/jobs
と解釈する
- 起動
json-server db.json --routes routes.json
curl 'http://localhost:3000/jobs/accounts/abcde?id_str=798314638660210688&id_str=798314638660210689' [ { "url": "http://test.com", "id_str": "798314638660210688", "status": "PROCESSING" }, { "url": "http://test.com", "id_str": "798314638660210689", "status": "PROCESSING" } ]
配列にも対応しているようですね
middleware
これはREADMEのような感じでは試さなかったけどレスポンスヘッダーをいじったりクエリパラメータをいじったり出来る
json-server db.json --middleware filename
で起動する
staticファイルの配置
- デフォルトだと./public以下へ配置
- gzipなども配信可能
静的ファイルもモックとして扱いたい場合がありそうなのでいいですね
module
middlewareやroutingなどちょっとだけいじりたいときならオプションでファイルを指定するのが早いのですが、色々組み合わせてやりたいときなどはmoduleを使って1枚で処理させることが出来ます
要はオプションで指定しているものをすべて1枚のjavascriptファイルにしてnodeで起動するといった流れですね
node server.js
こねくり回してみる
例文は本家に載っているのでそれを元にいじってみました(案件的に急ぎだったのであんまり修正してません。。。)
mockしたのは二つ
POST /jobs/accounts/:account_id GET /jobs/accounts/:account_id?ids_str=1111,222,333
流れとしては下記
- POSTでjobを作成しサービス側でレスポンスを保存する(job)
- 保存したデータを下にIDを使って問い合わせする(ids_str: カンマ区切りで複数)
- 問い合わせの内容次第でサービス側の挙動を変える(PROCESSING or SUCCESS)
一部手動でdb.jsonいじったりソース修正したりしないとモックできない箇所があるが動作確認するだけだったので特に綺麗にする気が起きず.....
サンプルとしてさらしておきます
- server.js
var jsonServer = require('json-server') var fs = require('fs'); var server = jsonServer.create() server.use(jsonServer.rewriter({ '/jobs/accounts/:account_id': '/jobs/:account_id' })) // '/1/stats/jobs/accounts/18ce53wl1u3': '/jobs' var router = jsonServer.router('db.json') var middlewares = jsonServer.defaults() // Set default middlewares (logger, static, cors and no-cache) server.use(middlewares) // Add custom routes before JSON Server router server.post('/jobs/:account_id', function(req,res){ console.log('posted job') // console.dir(req.params) mock_status = "PROCESSING" var d = new Date() mock_job_id = d.getTime() mock_response_json = { "request": { "params": { "start_time": "2016-11-11T00:00:00Z", "end_time": "2016-11-14T00:00:00Z" } }, "data_type": "job", "data": { "start_time": "2016-11-11T00:00:00Z", "url": null, "id_str": mock_job_id.toString(), "account_id": req.params.account_id, "status": mock_status, "created_at": "2016-11-15T00:00:16Z" } } var file = fs.readFileSync('db.json') var json = JSON.parse(file); var job = { "start_time": "2016-11-11T00:00:00Z", "url": null, "id_str": mock_job_id.toString(), "account_id": req.params.account_id, "status": mock_status, "created_at": "2016-11-15T00:00:16Z" } // json["jobs"] = [] //初期化 json["jobs"].push({id_str: mock_job_id, res: job }) fs.writeFileSync('db.json',JSON.stringify(json)); res.jsonp(mock_response_json) }) server.get('/jobs/:account_id', function(req,res){ var ids_str = req.query["job_ids"] if ( ids_str ){ var ids = ids_str.split(",") req.query = { id_str: ids } var file = fs.readFileSync('db.json') var json = JSON.parse(file); var res_jobs = [] ids.forEach(function(id){ var match = json["job"].filter(function(row,index){ return row.id_str == id }); if ( match.length == 1 ) { res_jobs.push(match[0]["res"]) } }); var res_data = { "request": { "params": { "job_ids": ids } }, "data_type": "job", "next_cursor": null, "data": res_jobs } } res.jsonp(res_data); }); // To handle POST, PUT and PATCH you need to use a body-parser // You can use the one used by JSON Server server.use(jsonServer.bodyParser) server.use(function (req, res, next) { console.log('get requested. params check') next() }) // Use default router server.use(router) server.listen(3000, function () { console.log('JSON Server is running') })
- db.json
{ "jobs": [ ] }
パス名とrewrite後のパスを合わせているが実際はスクリプト側から更新をかけているためあんまり意味ない感じですね
まとめ
パラメータ変えたり、パスを変えたり何でも出来ます
途中色々はまった中で気づいたのですが処理に順番がある
- rewriter
- custom routes
- body-parser
といった流れになってそう
同じパスだけどリクエストメソッドが違うパターンでレスポンスも分けたいパターンで結構はまり、結局パスとメソッド指定で特定の動作をさせるという形に落ち着きました
複雑なAPIのモックはちょっと時間がかかりそうですが簡単なRESTAPIたったら数分で起動できてしまうので便利ではないでしょうか