Node.js + Express でPOSTデータを取得後、WebAPIへ問い合わせる

とある Web APIを触っていたところ

* CORS設定により、ブラウザからのAPIリクエストが許可されていない。
* しかし、ブラウザからの入力値を受け取り、WebAPIへリクエストを送りたい。
* できればJavaScript(Node.js)で構築したい

というユースケースがあり、ちょっと試してみました。

# やること

- Webフォームからデータを入力
- Node.jsのサーバーへPOSTで送信
- Node.js サーバーからWebAPIへリクエストを送り、返り値をWebフォームへ送る


# 実装

1. Node.jsでPOST値を受け取るため、Expressでサーバーを立ち上げる
2. POSTで送られたデータをrequest モジュールでAPIへ送信
3. 返り値をPromiseで待受け、ブラウザへ返す

という流れで実装しました。

「Webフォームから入力した書籍名を、Google Books APIへ問い合わせし、返り値を返す」というシナリオで実装します。

(※Google Books API はブラウザからもリクエストを受け付けますが、参考実装ということで)

開発環境は以下のとおりです。

* OS
    * Ubuntu Linux (Windows Subsystem for Linux)
* Node
    * v10.16.0
* npm
    * 6.9.0
  


# Express とbody-parser のインストールとサーバー起動

最初に [Express](http://expressjs.com/) と [body-parser](https://github.com/expressjs/body-parser) のインストールを行う。インストールはnpmを利用

```
npm install express --save
npm install body-parser --save
```

インストール後、Expressの起動スクリプトを記述。ポート番号を3000で待ち受け。「http://127.0.0.1:3000/auth/」 のエンドポイントで、POSTリクエストを待ち受ける。
POSTで送られてきたbodyデータを、console.logで出力。


index.js

```javascript
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const port = 3000
app.use(bodyParser.urlencoded({
	extended: true
}));
app.use(bodyParser.json());
app.post('/auth/', (req, res) => {
  console.log(req.body);
  res.send("Received POST Data!");
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

```

Expressサーバーを起動後、エンドポンイト「auth」にPOSTを送ると、POSTの値が表示される。

curl でPOST送信

```bash
curl -X POST -H "Content-Type: application/json" -d '{"test":"test"}' 127.0.0.1:3000/auth
```

Express サーバーのコンソールに、POSTで送信されたJSON {"test": "test"} が表示されているのがわかる。

![2019-08-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/fd039765-a0e6-445c-736c-2fff55d8c268.png)


POST送信したクライアントには、「Received POST DATA!」という値が返される


![2019-08-04 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/bc05aa8e-5f7c-0910-f945-f5da8cd1d9a8.png)



# requestのインストールとWebAPIへのリクエスト

次に、[request](https://github.com/request/request) を利用した Web APIへのリクエストを実装する。
最初にrequestをインストール。

```bash
npm install request --save
```
次に、requestを利用して、Google Books API へ問い合わせるためのコードを記述。
Google Books API へ、ISBNコードを送信。レスポンスのJSONから、タイトルを表示する。
サンプルデータは、小説家・古川日出男さんの著書「[平家物語 犬王の巻](https://www.amazon.co.jp/%E5%B9%B3%E5%AE%B6%E7%89%A9%E8%AA%9E-%E7%8A%AC%E7%8E%8B%E3%81%AE%E5%B7%BB-%E5%8F%A4%E5%B7%9D%E6%97%A5%E5%87%BA%E7%94%B7/dp/4309025447/)」にしてみた。

ISBNコードは 
4309025447
となる。


request.js

```javascript
const request = require('request')
const options = {
	method: 'GET',
	json: true,
	url: "https://www.googleapis.com/books/v1/volumes?q=isbn:4309025447",
}
request(options, function(error, response, body) {
	console.log(body.items[0].volumeInfo.title);
});
```

上記のコードを実行すると、Google Books API の返り値からタイトルを取得し、コンソールへ表示を行う。

![2019-08-04 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/32059414-e89d-d1c4-0977-d96de94ae72d.png)


# フォームも含めた実装

上記の2つのコードを統合し、htmlの入力フォームを実装する。

- フォームからISBNコードを入力して、Express サーバーへPOST送信
- Express サーバーがPOSTデータをパースし、Google Books API へリクエスト送信
- Google Books API からのレスポンスからBodyを抽出し、JSONデータとしてhtmlフォームへ返却
- htmlフォーム側の console.log に書籍のタイトルを表示

という流れで実装しました。


## Express で静的ファイルをホスティングする

Express では、[「express.static」の指定を行うことで、静的ファイルのホスティングパスの指定ができる](http://expressjs.com/en/starter/static-files.html#serving-static-files-in-express) 。

Express と request のコードをまとめて実装。

index.jsは以下の通り。

「public」ディレクトリを静的ファイルの公開パスと設定。
「/public/index.html」 に保存したhtmlは

http://127.0.0.1:3000/ 

で表示されます。

```javascript
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const request = require('request')
const port = 3000
app.use('/', express.static('public'));
app.use(bodyParser.urlencoded({
	extended: true
}));
app.use(bodyParser.json());
app.post('/auth/', (req, res) => {
	const options = {
		method: 'GET',
		url: "https://www.googleapis.com/books/v1/volumes?q=isbn:" + req.body.number,
		json: true,
	};
	request(options, function(error, response, body) {
		console.log(body.items[0].volumeInfo.title);
		res.send(body);
	});
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

```


index.html。index.jsで指定した「auth」エンドポイントへ、POSTデータを送信。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="utf-8">
	<title>Node.js サンプルモック</title>
</head>
<body>
	<div>
		<h2>ISBNコード送信</h2>
		<div>
			<input id="isbn" placeholder="isbn" type="text">
		</div>
		<div>
			<button id="send">送信</button>
		</div>
	</div>
	<script>

	document.getElementById("send").onclick = function() {

	  const isbn = document.getElementById("isbn").value;
	  const json = {"number" : isbn};

	  fetch('http://127.0.0.1:3000/auth/', {

	    method: 'POST',
	    headers: {
	     'content-type': 'application/json',
	    },
	    body: JSON.stringify(json),

	  }).then(response => {

	   return response.json();

	  }).then(res => {

	   console.log(res.items[0].volumeInfo.title);
	   
	     }).catch(error => {

	       console.log(error);
	  });

	};

	</script>
</body>
</html>

```

Node.js で公開されたindex.htmlに対して、ISBNコード「4309025447」を入力後、送信すると、index.js側でGoogle Books APIに書籍情報を問い合わせ、Express サーバー、index.htmlのコンソールにそれぞれ書籍名が表示されます。


![console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/58994f7b-91f7-e4d9-8d46-baa28a460bdd.png)

![mock-form.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/c1ac4d63-6715-6203-8839-6f7d995a446d.png)


# 参照情報

- [Express 公式サイト](http://expressjs.com/)
- [Express 日本語サイト](https://expressjs.com/ja/)
- [Using Fetch API](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch)
- [body-parser](https://github.com/expressjs/body-parser)
- [request](https://github.com/request/request)
- https://qiita.com/ktanaka117/items/596febd96a63ae1431f8
- https://qiita.com/morou/items/06cbe49f64d56d31b793