LoginSignup
33
41

More than 5 years have passed since last update.

Node.jsとExpressでローカルサーバーを構築する(3) ―JSONを返すスタブAPI―

Last updated at Posted at 2019-02-27

リンク一覧

  1. Node.jsとExpressでローカルサーバーを構築する(1) ―Node.jsとnpmの導入―
  2. Node.jsとExpressでローカルサーバーを構築する(2) ―Expressでルーティング―
  3. Node.jsとExpressでローカルサーバーを構築する(3) ―JSONを返すスタブAPI―(本記事)
  4. Node.jsとExpressでローカルサーバーを構築する(4) ―他サーバーから画像を取得する―

最近は、Jsonデータを送りつけたり受け取ったりしてなんやかやするケースが増えてきました。私の極狭い認識の範囲では、フロント側の製作期間中にAPIが完成していないことが多いです。
その為、ローカルサーバーにJSONを返すスタブAPIの機能を実装したいと思います。

スタブとは - IT用語辞典

概要

この記事の概要

  • 目的
    • Node.jsとExpressを利用して、PC上にローカルサーバーを立ち上げる
  • 本記事のゴール
    • GETでリクエストを送ると、JSONデータが返ってくる
    • POSTでJSONデータを送ると、200 OK のレスポンスコードが返ってくる
  • 対象者
    • WEBフロント担当者
    • HTML,CSS,JavaScript(es2015含む)の基本的な構文を理解している人
    • HTTP通信、GET、POSTなどについて、ある程度理解している人(ざっくりで良いです)
    • 黒い画面にコマンドを打ち込むことに抵抗がない方
  • 環境・バージョン
    • Windows10
    • Node.js(推奨版) 10.15.01
    • npm 6.4.1
    • Express 4.16.4
    • Advanced REST client 10.0.12

Chrome拡張ツール:Advanced REST client

GETやPOSTの動作確認を簡単に行うために、Chrome拡張ツールの「Advanced REST client」を利用します。
Advanced REST client
導入方法や利用方法は、下記サイトが詳しいです。分かりやすいUIですので、本記事内でも都度説明します。
「Advanced REST client」の使い方まとめ ~GoogleChromeの拡張ツール

expressでのルーティングの実装

前回のおさらい
Expressを使ったサーバーで、静的コンテンツのルーティングのためにuseメソッドやexpress.staticメソッドを利用しました。

const express = require('express');
const app = express();

app.listen(8080);

app.use(express.static(path.join(__dirname, 'public')));

useは、全てのタイプのリクエストで実行されます。

ルーティングの基本

GETリクエストを受け取る

GETリクエストを受け取りたい場合は、getメソッドを利用します。

// サイトルートへのGETリクエストの場合
app.get('/', (req, res, next)=>{
  res.send('ルートだよ!');
});

// /hoge/fugaへのGETリクエストを受け取りたい場合
app.get('/hoge/fuga', (req, res, next)=>{
  res.send('ほげふが');
});

POSTリクエストを受け取る

POSTリクエストを受け取りたい場合は、postメソッドを利用します。

// サイトルートへのPOSTリクエストの場合
app.post('/', (req, res, next)=>{
  res.send('ルートだよ!全員集合!');
});

// /hoge/fugaへのPOSTリクエストを受け取りたい場合
app.post('/hoge/fuga', (req, res, next)=>{
  res.send('ほげふが');
});

パスの指定

getpostいずれの場合も、第一引数にはピックアップしたいサイト上のパスを指定します。この指定は、ワイルドカードや正規表現などを組み合わせることも可能です。

app.get(/^book/, ()=>{});

上記の例では、/book/booksにはマッチしますが、/boo/ibookとはマッチしません。
公式ガイド:Expressでのルーティング

ミドルウェアの基本

getpostいずれの場合も、第二引数はリクエストを受け取った際のコールバック関数を渡します。このコールバック関数は、ミドルウェア(中間処理を行う関数)と呼ばれます。
ミドルウェアは、リクエストreq・レスポンスresを表すオブジェクトと、後続のミドルウェアへのバトンの役割を果たす関数nextを受け取ります。
このミドルウェア内でクライアントにレスポンスをするか、明示的にプロセスを終了させるかnext()を実行して他へ制御を渡すかしないと、応答なしの状態に陥ってしまいます。

app.get(/^book/, (req, res, next)=>{

  if(req.query.id){
    res.send('OK.');
  }else{
    next();
  }
});

res.send
クライアントへHTTPレスポンスを返すためのメソッドです。
文字列だけでなく、配列やオブジェクトも送信できます。事前に指定しなかった場合、ヘッダーのContentTypeは送信データに応じて自動的に割り当てられます。

res.end
データ送信をせずにレスポンスのプロセスを終了させるためのメソッドです。
実体はNode.jsのコアモジュールhttpresponse.endです。引数にデータを渡せば送信されますが、res.sendなどの専用メソッドを利用する方が望ましいです。

公式ガイド:アプリケーションで使用するミドルウェアの作成

送信されたデータを受け取る

GETで送信されたURLパラメータ

GETで送信されたURLパラメータを受け取るには、ミドルウェアに渡されたreqオブジェクトのqueryプロパティにアクセスします。

/**
 * /search?color=red&size=small へのリクエスト
 */ 
app.get('/search', (req, res, next)=>{

  console.log(req.query.color); // "red"
  console.log(req.query.size);  // "small"

});

リファレンス:req.query(英語)

POSTで送信されたJOSNデータ

POSTでJSONデータを受け取るには、予めパース処理を行うミドルウェアを設定しておく必要があります。このミドルウェアは、Expressに予め用意されています。

const express = require('express');
const app = express();

app.use(express.json()); //パース用ミドルウェアを設定

app.post((req, res)=>{
  console.log(req.body); //パースされたデータを参照
});

Expressの静的メソッドjsonの実行結果をuseメソッドに渡しています。渡すのはあくまでjsonの実行結果として返されるミドルウェアで、jsonメソッドそのものではありません。パースされたデータは、requestオブジェクトのbodyプロパティに格納されます。

リファレンス:express.json(英語)

JSONデータを送る

オブジェクトをパースして送る

オブジェクトをJSONにパースした上で送信したい場合は、res.jsonメソッドを使います。

app.get('/', (req, res, next)=>{
  res.json({ message: `This is a pen.`});
});

リファレンス:res.json(英語)

 JSONファイルを送る

予め用意したJSONファイルを送信したい場合、res.sendFileメソッドが便利です。このメソッドは、Expressのバージョン4.8.0以降からサポートされています。

app.get('/', (req, res, next)=>{
  res.sendFile(__dirname + '/data.json', (err) => {
    if (err) {
      res.sendStatus(400);
    } else {
      console.log('sending completed');
    }
  });
});

第一引数に送信したいファイルの絶対パスを指定します。
第二引数には、送信完了もしくはエラー発生時に呼び出されるコールバック関数を指定します。
エラー発生時はエラー情報を引数で受け取ります。成功時はundefinedです。

リファレンス:res.sendFile(英語)

route()でミドルウェアをまとめる

routeメソッドを利用すると、同一パスに対するタイプ別のリクエストに対し、ミドルウェアを見通し良くまとめられます。

app.route('/moimoi')
  .get((req, res)=>{
    // GET
  })
  .post((req, res)=>{
    // POST
  })
  .put((req, res)=>{
    // PUT
  })
  .delete((req, res)=>{
    // DELETE
  });

リファレンス:app.route(英語)

 Routerでルーティング処理を外部モジュール化する

ルーティング処理を他ファイルで定義して、メインの処理にミドルウェアとして組み込むことが出来ます。

ミドルウェアを定義して公開する

Expressの静的メソッドexpress.Routerを実行してルーティング用のミドルウェアの骨格を生成します。
この骨格に対して、「このパスへのリクエストはこれ返せ」といったルーティング処理を肉付けしていきます。肉付けの方法は、通常のexpress()で取得したオブジェクトと同様に、use、get、post等を利用します。
そして、完成したミドルウェアをエクスポートして公開します。

/**
 *  /api/index.js
 */
const express = require('express');
const router = express.Router(); //ミドルウェアの準備

router.get('/items', (req, res) => {
  res.json({ items: [] });
});

router.post('/registration', (req, res) => {
  res.end('Completed');
});

module.exports = router; //エクスポートして公開

公開されたミドルウェアを利用する

公開されたミドルウェアをインポートしてルーティングに適用します。

/**
 * /app.js
 */
const express = require('express');
const app = express();
const api = require('./api/'); //ミドルウェア読み込み

app.use('/api', api);

/apiへのリクエストの対応を、/api/index.jsで定義したミドルウェアへ渡しています。
この際、ミドルウェア内のルートパスは/apiです。ミドルウェア内で指定した/itemsは、実際は/app/itemsへのリクエストです。ミドルウェア内で/app/itemsとしてしまうと、実際には/app/app/itemsを指定したことになってしまいます。

ガイド:express.Router(日本語)
リファレンス:express.Router(英語)

API機能の実装

上記を踏まえて、API機能を実装していきます。

 ファイルとフォルダの準備

API関連のファイルを格納するフォルダapiを追加します。
このフォルダの配下に、クライアントに送りつけるjsonデータと、ルーティング処理を記述するjsファイルを作成します。

sample/
  ├ public/
  │    ├ css/
  │    │   └ sample.css 
  │    ├ js/
  │    │   └ sample.js 
  │    ├ img/
  │    │   └ sample.png 
  │    └ index.html
  ├ api/
  │    ├ index.js
  │    └ data.json
  └ app.js
/**
 * /app.js
 */
const express = require('express');
const app = express();
const path = require('path');
// /api/index.js で定義されたミドルウェア
const api = require('./api/');

app.listen(8080, () => {
  console.log('Running at Port 8080...');
});

// APIルーティング用ミドルウェアを/apiに設定
app.use('/api', api);

app.use(express.static(path.join(__dirname, 'public')));

app.use((req, res) => {
  res.sendStatus(404);
});


/**
 * /api/index.js
 */
const express = require('express');
const router = express.Router();

// JSONパース
router.use(express.json());

// /api/foo へのGETリクエスト
router.get('/foo', (req, res) => {
  // ファイルを転送
  res.sendFile(__dirname + '/data.json', (err) => {
    if (err) {
      res.sendStatus(400);
    } else {
      console.log('sending completed');
    }
  });
});

// /api/bar へのGET・POSTリクエスト
router.route('/bar')
  .get((req, res) => {

    // 受け取ったパラメータをそのままJSONにして送り返している
    res.json(req.query);
  })
  .post((req, res) => {

    // 必須のデータ項目を、id,name,address として、受信データをチェックしている
    const nameAry = ['id', 'name', 'address'],
      failed = nameAry.some(v => !req.body[v]);

    if (failed) {
      res.sendStatus(400);
    } else {
      res.sendStatus(200);
    }
  });

module.exports = router;

/api/data.jsonに適当な内容を記述します。

{
  "id": "W0001",
  "title": "I Love Cats and Dogs",
  "price": 3000000000000
}

準備が出来たら、サーバーを起動します。

$ node app.js

「Advanced REST client(ARC)」で送受信テスト

送受信のテストを、Chromeの拡張ツールの「Advanced REST client」で行いたいと思います。

こちらから取得できます。
ホーム > アプリ > Advanced REST client

ARCを起動して、まずはTOPページにアクセスしてみます。

  1. Methodを「GET」
  2. RequestURLに「http://localhost:8080/
  3. 「SEND」をクリック

arc.png

「200 OK」とHTMLデータが取得できました。

では、続けてAPIのデータを取得します。
GETで「http://localhost:8080/api/foo」へリクエストを送ると、/api/data.jsonが取得できます。

次は、GETで「http://localhost:8080/api/bar?name=pee&address=poo&age=17」へリクエストを送ります。
URLパラメータの値をJSONで取得で取得できます。

次は、POSTで「http://localhost:8080/api/bar」へリクエストを送ります。

  • 「Parameters>Body>Body content type」を「application/json」
  • 「Parameters>Body>Editor view」を「JSON visual editer」
  • id, name, address の三つのプロパティを含むJSONデータを定義

arc02.png

「200 OK」が返ってきました。
次は、項目を一つ減らして送信してみると、「400 Bad Request」が返ってきます。

これで、APIの開発が間に合っていなくても何とかなりそうですね!

初心者の拙い記事を読んでいただき、ありがとうございました。
お役に立てていたら幸いです。

JSON Server

この記事を書いている途中で、JSONを返すAPIを簡単に実現する「JSON Server」なるものの存在を知りました。。。
JSON Server使いこなし - モックサーバーの起動とリソース処理 | CodeGrid
公式:GitHub - typicode/json-server

参考情報

Express公式サイト(英語)
Node.js + ExpressでREST API開発を体験しよう[作成編]

33
41
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
41