【演習】IBM CloudでNode.jsアプリを構築する - Part3: Expressアプリ

#はじめに
これはIBM Redbooksの[Developing Node.js Applications on IBM Cloud](http://www.redbooks.ibm.com/abstracts/sg248406.html)をなぞってみたものです。
この記事では第3章の「Creating your first Express application」に取り組んでいきます。

ExpressはNode.jsのWebフレームワークです。これを使ってWebアプリケーションの構築を早めることができます。

この記事では、ExpressフレームワークとWatson Natural Language Understandingサービスを利用してWebに掲載された記事から指定の情報を引き出します。アプリケーションに記事のURLを与えることでその筆者の情報を得ます。

#必要物
- IBM Cloudアカウント
- インターネットに接続できる環境
- Google Chrome または Firefox
- OS: Linux, Mac OS または Windows

# IBM Cloud上にNode.js環境を立ち上げる
まずはIBM Cloudにログインします。
すると、ダッシュボードが表示されるので、右上の「Create resource」をクリックします。
(あるいは右上の「カタログ」をクリックします。)

カタログに移動したら、左のメニューから「Cloud Foundryアプリ」を選ぶか検索バーに「Node.js」を入力し、「SDK for Node.js」サービスを選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/b0a5d2f2-ea5a-1292-f30a-3a1d9bb924c4.png)

任意のアプリ名を入力し、右下の「作成」をクリックします。
このアプリ名はIBM Cloud上の他のアプリと被らないものでなくてはいけません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/0bece5f9-8dea-1588-bee2-f202ad3162fd.png)

サービスの利用準備ができたら、アプリ名の横の表示が緑に変わりますので、それまで数分待ちます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/d5d0fdb5-0407-ee4f-72a8-2666b45afafc.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/9caaef39-0218-2efb-b58c-28053c63ea20.png)

# Continuous Deliveryを有効化する
「概要」タブに移動します。
下部にある「継続的デリバリー」を「有効化」ボタンを押して有効化します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/b85a8acf-29ad-8a5d-4e6d-a1b50eb34c46.png)

Continuous Deliveryツールチェーンの作成画面が現れますので、「地域」と「組織」を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/5acbf109-0fcf-ff21-b1a0-e0c2f22d569d.png)

下にスクロールして、「ツール統合」欄を設定します。
「リポジトリタイプ」はデフォルトでは「クローン」が選択されていますが、この演習では「新規作成」に変更します。そのほかの設定はそのままにして「作成」をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/bfd6ff35-e794-ca94-144b-690621d6e7f0.png)

作成完了後、以下のような画面が表示されます。
ツールチェーンのEclipse Orion Web IDEをクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/af23743a-d177-5ce2-eb9e-59c324cbaf52.png)

# Expressを使ってHelloWorldアプリを作成する
ExpressはNode.jsのフレームワークです。その核となるコンポーネントを「route」と言います。
routeは、アプリケーションのエンドポイントとクライアントのリクエストへの返答を定義します。
ExpressはHTTPメソッドと対応関係を持つルーティングメソッドをサポートします。

この後の流れでは、"Hello Express!!"と返すアプリケーションを作っていきます。

左側のメニューにあるアプリ名を右クリックして、「新規作成」→「ファイル」を選択します。
ファイル名を「package.json」としてEnterを押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/20195e0e-2a8c-4bf1-9e59-cfa7cb95d223.png)

作成できたら、「ファイル」->「保存」で保存します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/69992f8e-0c8f-23a5-05d5-ad57ce96b710.png)

package.json内に以下のように記述します。

```package.json
{
    "name": "<アプリ名>",
    "version": "0.0.1",
    "description": "A sample express app",
    "dependencies": {
        "express": "4.*"
    }
}
```

左側のメニューにあるアプリ名を右クリックして、「app.js」ファイルを新規作成します。
このファイルにアプリケーションのスタートポイントとなるExpressインスタンスを構築していきます。

以下のように記述します。
「GET /」は、アプリケーションのルートのリクエストを受けた時に"Hello Express!"を返します。
「POST /author」は/authorにPOSTリクエストが来た際に記事の筆者名を返します。

```app.js
var port = process.env.VCAP_APP_PORT || 8080;

//Express Web Framework, and create a new express server
var express = require('express'),
	app = express();
	
// In case the caller calls GET to the root '/', return 'Hello Express!'.
app.get('/', function(req, res) {
    res.send('Hello Express!');
});

// In case the caller calls POST to /author, return 'Author name'
app.post('/author', function(req, res) {
    res.send('Author name');
});

// start server on the specified port and binding host
app.listen(port);
```

左側のメニューにあるアプリ名を右クリックして、「新規作成」→「ファイル」を選択します。
ファイル名を「manifest.yml」としてEnterを押します。

ファイルに以下のように記述します。
各項目は各々の環境に合わせて変更してください。
ホスト名は変更していなければアプリ名と同じものになっているはずです。
commandはアプリケーションがステージングされた後にapp.jsを呼び出すのに用いられます。

```manifest.yml
applications:
- path: .
memory: 256M
instances: 1
domain: mybluemix.net
name: <アプリ名>
host: <ホスト名>
disk_quota: 1024M
command: node app
```

ドメイン(domain)はサービス作成時のリージョンによって変更する必要があります。

```
米国南部 US SOUTH -> mybluemix.net
英国　UNITED KINGDOM -> eu-gb.mybluemix.net
シドニー SYDNEY -> syd.mybluemix.net
ドイツ　GERMANY -> eu-de.mybluemix.net
```

ツールバーの中に、「新規起動構成の作成」というドロップダウンがありますので、その中の「＋」をクリックします。出てきた設定画面の設定を確認して、正しければ確定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/6b79ad37-83ed-17e7-4d18-a0daf84a38bf.png)

三角形のデプロイボタンをクリックします。
再デプロイの確認画面が出たらOKをクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/cb5914a3-0e28-f273-f4fc-749042be1573.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/c9c2db15-acfd-bfa9-fd70-23db092e777a.png)

デプロイをしている間は起動構成の部分が「デプロイ中」になります。
デプロイが終わったら同部分は緑の丸がつき、「実行中」と表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/9960dd0e-880d-a269-09cb-33a51e15923e.png)

デプロイ終了後、ライブ編集がオフになっていることに気づくかもしれません。
ライブ編集はNode.jsアプリケーションにのみ有効です。
manifest.ymlを利用してNode.jsアプリケーションを作成していることが認識されたらライブ編集はオンになります。
（IBM Cloudライトアカウントにはライブ編集機能は適用されていません）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/5234d4ea-bade-a01b-7205-2aaddc7358cb.png)

デプロイが終わったら、デプロイされたアプリケーションを開きます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/36bc4228-c6a7-99e9-ff1d-304f5403963b.png)

「Hello Express!」のメッセージが表示されたでしょうか？
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/b2e62b47-6867-6ee4-5d92-6dfebfae40da.png)

# HTMLでページを作成する
ここからはHTMLを使って、調べたい記事のURLを入力するフォームページを作っていきます。

viewsフォルダを作成し、その中にindex.htmlファイルを作成します。
以下のコードを記述します。

```index.html
<html>
	<body>
	    <h1 style="color:blue;">Watson Author Finder</h1>
	    <p>To get information about the author of an article, enter the URL of that article.</p>
	    <form action="author" method="post">
	        <input type="text" name="url" />
	        <input type="submit" value="Submit" />
	    </form>
	</body>
</html>
```

既存のapp.jsファイルを編集してindex.htmlファイルが呼び出されるようにします。

```app = express();``` とある行の下に ```var path = require('path');``` と書き足します。

```app.get('/', function(req, res) { ``` の中を ```res.sendFile(path.join(__dirname, 'views/index.html'));``` に変えてコールバック関数をアップデートします。

同部分の前のコメントを書き換えます。

```
// In case the caller calls GET to the root '/', 
// return the content of index.html
```

リクエストパラメータを受け取ることができるようにするために、body-parserモジュールを加えます。
このモジュールは、データをパースしたり、req.bodyモジュール下のデータを伴うオブジェクトを引き出したりします。

```var path = require('path');``` という行の下に以下を加えます。

```
var bodyParser = require('body-parser');

//parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
```

全体としては以下のようになります。

```app.js
var port = process.env.VCAP_APP_PORT || 8080;

//Express Web Framework, and create a new express server
var express = require('express'),
	app = express();
var path = require('path');
var bodyParser = require('body-parser');

//parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
	
// In case the caller calls GET to the root '/', 
// return the content of index.html
app.get('/', function(req, res) {
	res.sendFile(path.join(__dirname, 'views/index.html'));
});

// In case the caller calls POST to /author, return 'Author name'
app.post('/author', function(req, res) {
    res.send('Author name');
});

// start server on the specified port and binding host
app.listen(port);
```

この時、body-parserモジュールがpakcage.jsonファイルで定義されていないというメッセージが出ますので、「package.jsonの更新」を押して自動で更新を行います。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/cc90bc91-e3b6-d99e-4a7d-05902c81eff0.png)

pakage.jsonを開くと、その内容が上の画像のものから下の画像のものに更新されたことがわかるでしょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/b263d3e6-0901-4542-1f2c-b671c645f9b1.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/77e10402-59b7-b58f-1c4d-f2f4748ab3d3.png)

引き続き、app.jsファイルのコールバック関数を更新していきましょう。
```res.send('Author name');``` の行を ```res.send('You called the server requesting the author of the article: ' + req.body.url);``` に書き換えます。

その前にあるコメントを ```// In case the caller calls POST to /author, return the url of the article``` に更新します。

Shift + Alt + Fでフォーマットしてくれるってことなんですが、Macだとどのキーになるのかよくわからなかったので断念

app.jsのコードは全体としては以下のようになります。

```app.js
var port = process.env.VCAP_APP_PORT || 8080;

//Express Web Framework, and create a new express server
var express = require('express'),
	app = express();
var path = require('path');

var bodyParser = require('body-parser');

//parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({
	extended: false 
}));
	
// In case the caller calls GET to the root '/', 
// return the content of index.html
app.get('/', function(req, res) {
	res.sendFile(path.join(__dirname, 'views/index.html'));
});

// In case the caller calls POST to /author, return the url of the article
app.post('/author', function(req, res) {
    res.send('You called the server requesting the author of the article: ' + req.body.url);
});

// start server on the specified port and binding host
app.listen(port);
```

routesというフォルダを新規に作成します。
routesフォルダ内にindex.jsというファイルを新規作成します。
index.jsファイルに以下のコードを記述します。

```index.js
// index.js - Index route module
var express = require('express');
var router = express.Router();

//Provides utilities for dealing with directories
var path = require('path');

// Home page route
router.get('/', function (req, res) {
   res.sendFile(path.join(__dirname, '../views/index.html'));
});

module.exports = router;
```

author.jsというファイルをroutesフォルダ内に新規作成します。
以下のコードを記述します。

```author.js
// author.js - Author route module
var express = require('express');
var router = express.Router();

router.post('/', function (req, res) {
     res.send('You called the server requesting the author of the article: ' + req.body.url);
});

module.exports = router;
```

再び既存のapp.jsファイルを編集していきます。
以下の記述がある行を

```
// In case the caller calls GET
// start server on the specified port and binding host.
```

下のように書き換えます。

```
//Routes modules
var index = require('./routes'),
    author = require('./routes/author');
    
//In case the caller access any URI under the root /, call index route
app.use('/', index);

//In case the caller access any URI under /author, call author route
app.use('/author', author);

// start server on the specified port and binding host
```

全体としては以下のようになります。

```app.js
var port = process.env.VCAP_APP_PORT || 8080;

//Express Web Framework, and create a new express server
var express = require('express'),
	app = express();
var path = require('path');

var bodyParser = require('body-parser');

//parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({
	extended: false 
}));
	
//Routes modules
var index = require('./routes'),
    author = require('./routes/author');
    
//In case the caller access any URI under the root /, call index route
app.use('/', index);

//In case the caller access any URI under /author, call author route
app.use('/author', author);

// start server on the specified port and binding host
app.listen(port);
```

できたら、デプロイしてアプリを開きます。
下の画像のような入力ボックスが出てくるかと思います。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/0bd51401-8e99-7d97-886a-709aa736a888.png)

任意のURLを入れてSubmitを押すと、以下のように「You called the server requesting the author of the article: <URL>」と出てくるでしょう。
ちなみに私が入れたのは、このQiita記事の原本となる英語記事です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/084244ab-d3d0-98fd-4d14-e15be7369fe1.png)

# Watson Natural Language Serviceと連携させる
ここではWatson Natural Language Serviceを用いて記事の著者名を取得することを試みます。IBM Watson ServiceのREST APIを用いることで、自身のアプリケーションにWatsonサービスを加えることができるようになります。

Natural Language Understandingサービスでは自然言語処理を使って、プレインテキスト、HTML、パブリックURLなどのあらゆるテキストを分析します。

Node.jsでは「watson-developer-cloud」としてすでに3rdパーティモジュールとしてWatsonサービスを用いることができるようになっています。

それでは、Watsonサービスを作成中のアプリにバインドしていきましょう。
まずは、IBM Cloudの右上にある「カタログ」を押してカタログページを開きます。
左側のメニューから「Watson」を選ぶ、または検索バーに「Natural Language Understanding」と入力して、「Natural Lanaguage Understanding」サービスを選択しましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/8f658c9b-1970-c21c-09ae-62e8ff84100d.png)

サービス名の語尾についている記号を外し、サービス名を「Natural Language Understanding」としたら、「作成」をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/48e381c8-8d05-d6a6-77d0-0b7b99a057eb.png)

WatsonサービスあるいはNode.jsサービスの「接続」タブを選択し、「接続の作成」をクリックして、Node.jsとWatsonサービスをバインドします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/fb1fa711-ff9f-770a-7065-422ff3c869f8.png)

アプリの再ステージを問われるので、「再ステージ」を選択します。
完了したら、再びファイルの編集作業に戻ります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/f24def52-9c41-a139-4274-5b2eeff8521e.png)

package.jsonファイルを開きます。
dependenciesの ```"body-parser": "latest"``` を ```"body-parser": "*",``` とします。
同じくdependenciesの最下欄に「"watson-developer-cloud": "2.25.1"」と加えます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/cbbe41da-1968-75ac-9b91-1cfc07b91d23.png)

articleServicesというモジュールを作っていきます。このモジュールはWatsonサービスと連携して、与えられたURLから著者名を引き出すという動作をするようになります。

左のメニューのアプリ名を右クリックし「services」というフォルダを新規作成します。
servicesフォルダ内に「articleServices.js」というファイルを新規作成します。
articleServices.jsファイルに以下のコードを記述します。

extractArticleAuthorNames関数はWatsonサービスを呼び出します。


```articleServices.js
// Watson Natural Language Understanding third party module
//Specify the release for the Natural Language Understanding service
var NaturalLanguageUnderstandingV1 = 
require('watson-developer-cloud/natural-language-understanding/v1.js');
var natural_language_understanding = new NaturalLanguageUnderstandingV1({
	'version_date': NaturalLanguageUnderstandingV1.VERSION_DATE_2017_02_27
});
```

routesフォルダ内のauthor.jsファイルを開きます。
先ほどアプリを開いた際はURLが表記された部分が著者名になるように変えていきます。

上部の変数が並んだ行の下に ```var articleServices = require('../services/articleServices');``` を加えます。

また、 ```res.send('You called the server 
requesting the author of the article: ' + req.body.url);``` を、次のように書き換えます。

```
    articleServices.extractArticleAuthorNames(req, function(err, response) {
        if (err)
            res.status(500).send('error: ' + err);
        else
            res.send(response);
    });
```

全体としては以下のようになります。

```author.js
// author.js - Author route module
var express = require('express');
var router = express.Router();

var articleServices = require('../services/articleServices');

router.post('/', function (req, res) {
    articleServices.extractArticleAuthorNames(req, function(err, response) {
        if (err)
            res.status(500).send('error: ' + err);
        else
            res.send(response);
    });
});

module.exports = router;
```

# アプリのデプロイと実行
これまでのデプロイ方法ではなく、GitリポジトリにPushし、自動的にIBM Cloud Delivery Pipelineにビルド・デプロイさせる方法について説明します。

 Eclipse Orion Web IDEの左側にある鉛筆マークの一つ下のGitアイコンをクリックします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/30c1d98c-57e5-2f37-afc9-980db59a5ff2.png)

「コミットメッセージを入力」とあるテキストボックスに「Watson Author Finder - Initial Code」など任意のメッセージを入力し、右上の「コミット」をクリックします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/3bf3304d-22ce-c7e6-af0c-ce4fffcb8906.png)

左部の発信欄にコミットした内容が移りますので、その右上の「プッシュ」をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/c7d4267f-7cbc-21fd-c795-11910af66aae.png)

アプリケーションがGitにプッシュされると、Delivery Pipelineは自動でビルド・デプロイを行います。

左上の矢印ボタンを押して、ツールチェーンに戻り、Deliver欄のDelivery Pipelineをクリックします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/d797f9d2-b825-80af-332b-def11689dded.png)

Build StageとDeploy Stageの両方が終わるまで待ちます。失敗したら、各ステージの右上の三角ボタンを押してもう一度実行してみましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/8c307505-ed62-9927-be20-6362618b4e9a.png)

Deploy Stageの最終実行の結果欄のアプリ名のところにあるリンクを押し、アプリを立ち上げます。

以下のような画面が現れます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/3dad776a-f4bc-4d31-a81b-08c05ce26d83.png)

確実に著者名の記録のあるリンクとして、例えば、次のリンクをテキストボックスに入力して、Submitを押します。
https://www.forbes.com/sites/alexkonrad/2016/01/29/new-ibm-watson-chief-david-kenny-talks-his-plans-for-ai-as-a-service-and-the-weather-company-sale

すると、以下のような画面が現れます。著者名が取得できていることがわかります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/141025/84c881d6-c6a4-e40c-0256-5caaeb41687e.png)

# おわり
今回の内容は以上です。
原本である[Developing Node.js Applications on IBM Cloud](http://www.redbooks.ibm.com/abstracts/sg248406.html)には続章がありますので、引き続きまとめられたらいいなと思っています。