Node.jsを使って、ちょっとREST APIが作れるフレームワークを試してみようと思いまして。
Expressというフレームワークがスタンダードらしいので、これを試してみることにします。
ドキュメントをざっと見た感じ、シンプルで使いやすそうな雰囲気が?
以前にNode.jsのhttpモジュールを使って試したようなこと(JSONを受け取って四則演算を行うAPIを作ってみる)を、Expressを使って書いてみましょう。
Node.jsで、JSONをPOSTするHttpClient/受けるHttpServerを書いてみる - CLOVER🍀
環境
対象の環境は、このようになっています。
$ node -v v9.5.0 $ npm -v 5.6.0
準備
とりあえず、チュートリアルを見ながらインストール。
$ npm install --save express
package.jsonでの依存関係は、このように。
"dependencies": { "express": "^4.16.2" },
「Hello World!!」
「Hello World」のサンプルを見ながら、簡単なHTTPサーバーを書いてみます。
Hello world example
こんな感じで。
src/simple.js
const express = require("express"); const app = express(); app.get("/", (req, res) => res.send("Hello World")); app.listen(3000);
リッスンポートは3000で。
package.jsonにはこういう記述にして
"scripts": { "simple": "node src/simple.js"
実行。
$ npm run simple > hello-rest-api@1.0.0 simple ... > node src/simple.js
確認。
$ curl http://localhost:3000 Hello World
OKそうですね。というか、簡単…。
ちなみに、Application#listenの戻り値はhttp.Serverらしいので、停止する時はこれを受け取ってstopを呼べばよいようです。
app.listen
こんな感じで。
const httpServer = app.listen(3000); // ... httpServer.stop();
JSONを受け取るサーバーを書いてみる
続いて、JSONを受け取るサーバーを書いてみましょう。POSTで受け取ることにします。
こちらを見ると、JSONをリクエストとしてパースするには、body-parserというmiddlewareが必要なようです。
req.body
とはいえ、Expressをインストールしていると、そのまま使えるようですが。
ちょっと書いてみましょう。
src/rest-server.js
const express = require("express"); const bodyParser = require("body-parser"); const app = express(); app.use(bodyParser.json()); app.post("/calc", (req, res) => { const calc = req.body; console.log(`[${new Date()}] request = [${JSON.stringify(calc)}]`); const operator = calc.operator; const a = calc.a; const b = calc.b; const calculator = () => { if (operator === "+") { return a + b; } else if (operator === "-") { return a - b; } else if (operator === "*") { return a * b; } else if (operator === "/") { return a / b; } else { return `Unknown operator[${operator}]`; } }; res.json({"result": calculator()}); }); app.listen(3000);
Application#useで、bodyParser#jsonを使うようにすればよい、と。
const express = require("express"); const bodyParser = require("body-parser"); const app = express(); app.use(bodyParser.json());
この状態で、「application/json」なリクエストを受け付けると、JSONパースしてくれるようです。
app.post("/calc", (req, res) => { const calc = req.body; console.log(`[${new Date()}] request = [${JSON.stringify(calc)}]`); const operator = calc.operator; const a = calc.a; const b = calc.b;
レスポンスは、Response#jsonで返すとJSONにしてくれるようです。
res.json({"result": calculator()});
このスクリプトを、「npm run」で起動するようにしてみましょう。
"scripts": { "simple": "node src/simple.js", "rest": "node src/rest-server.js",
確認。
$ curl -H 'Content-Type: application/json' http://localhost:3000/calc -d '{ "operator": "+", "a": 3, "b": 4 }' {"result":7}
OKそうです。
テストを書いてみる
蛇足ですが、このサーバーを起動した状態で、テストを書いて確認してみましょう。テスト中にサーバーを起動して停止して…とかいうのは、考えないことにします。
テストのランナーとしてはAVAを
GitHub - avajs/ava: Node.js test runner that lets you develop with confidence 🚀
HTTPリクエストの送信には、「request-promise」を使用することにします。
GitHub - request/request-promise: The simplified HTTP request client 'request' with Promise support. Powered by Bluebird.
インストール。
$ npm install --save-dev ava@next chai request request-promise
package.jsonでの依存関係は、このように。
"devDependencies": { "ava": "^1.0.0-beta.1", "chai": "^4.1.2", "request": "^2.83.0", "request-promise": "^4.2.2" }
テストコードは、こんな感じで書きました。
test/rest-server-test.js
import test from "ava"; import chai from "chai"; import rq from "request-promise"; chai.should(); const optionsBasic = { method: "POST", uri: "http://localhost:3000/calc", json: true }; test("calc plus", t => { const options = Object.assign({}, optionsBasic); options.body = { operator: "+", a: 8 , b: 3 }; return rq(options) .then(response => { response.result.should.equal(11); t.pass(); }); }); test("calc minus", t => { const options = Object.assign({}, optionsBasic); options.body = { operator: "-", a: 8 , b: 3 }; return rq(options) .then(response => { response.result.should.equal(5); t.pass(); }); }); test("calc multiply", t => { const options = Object.assign({}, optionsBasic); options.body = { operator: "*", a: 8 , b: 3 }; return rq(options) .then(response => { response.result.should.equal(24); t.pass(); }); }); test("calc division", t => { const options = Object.assign({}, optionsBasic); options.body = { operator: "/", a: 8 , b: 2 }; return rq(options) .then(response => { response.result.should.equal(4); t.pass(); }); }); test("calc invalid operator", t => { const options = Object.assign({}, optionsBasic); options.body = { operator: "?", a: 8 , b: 3 }; return rq(options) .then(response => { response.result.should.equal("Unknown operator[?]"); t.pass(); }); });
package.jsonでは、AVAを実行するようにして
"scripts": { "simple": "node src/simple.js", "rest": "node src/rest-server.js", "test": "ava -v" },
別コンソールでサーバーを起動した状態で、確認。
$ npm test > hello-rest-api@1.0.0 test ... > ava -v ✔ calc plus ✔ calc minus ✔ calc multiply ✔ calc division ✔ calc invalid operator 5 tests passed
OKですね。