Node-REDのfunctionノードで外部npmモジュールを呼び出す方法

　日立製作所OSSソリューションセンタの横井一仁です。今回は、夏に開催した[Node-RED v2.0新機能紹介イベント](https://node-red.connpass.com/event/220079/)(下の動画)の中で紹介したNode-REDのfunctionノードの機能を少し深堀してみます。

https://www.youtube.com/embed/JklLkNr3Ec8?start=1967

<iframe width="560" height="315" src="https://www.youtube.com/embed/JklLkNr3Ec8?start=1967" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

　最新のNode-RED v2.1.4のfunctionノードのプロパティ設定を見ると、いつの間にか「設定」、「初期化処理」、「コード」、「終了処理」のタブが増えています。

![config.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287766/3874e15f-d4d4-4769-a989-75a519e02c69.png)

今回は、このうち「設定」タブが持つ「外部モジュールのインストール機能」を試してみます。

## 外部npmモジュール利用の背景と課題
　JavaScriptのコードを直接記載できるfunctionノードは、昔から外部のnpmモジュールを利用することは可能でしたが一手間必要でした。具体的には、[公式ドキュメント](https://nodered.jp/docs/user-guide/writing-functions#functionglobalcontext-%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E5%88%A9%E7%94%A8)に記載されているように、一度Node-REDのプロセスを終了し、設定ファイルsettings.jsを修正した後、npm installコマンドを用いてnpmモジュールをインストールする必要がありました。この手順は、ローカル環境のNode-REDであれば、フローを開発する開発者自身が行えますが、SaaSで提供されているNode-RED環境の場合は、Node-REDのインスタンスを管理しているサービス提供者に設定作業を依頼する必要がありました。

## 外部npmモジュールインストール機能による解決
　本問題を解決するために、Node-RED v1.3からfunctionノードのプロパティ上で外部npmモジュールをインストールできるようになりました。Node-RED v1.3では試験的に本機能が取り込まれたため、本機能はデフォルトで無効になっていましたが、ユーザリクエストが多数あり、Node-RED v2.0からデフォルトで有効になりました。

## 牛のアスキーアートを表示するnpmモジュールを利用してみる
　functionノードをワークスペースに配置してプロパティ設定画面を開くと、「設定」タブにnpmモジュールを設定できるUIが存在していることが分かります。左下の「追加」のボタンをクリックすると、外部npmモジュールの名前を入力できる項目が表示されます。ここでは、牛のアスキーアートを表示するnpmモジュール「cowsay2」を入力してみました。

![cowsay2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287766/4d24eeaf-ecde-3751-1bb4-8c64a056ba26.png)

　この時、自動入力される右隣の「cowsay2」は、JavaScriptのコード内で利用できる変数名となります。

　次に「コード」タブ内で、cowsay2モジュールを呼び出すコードを記載します。cowsay2モジュールは、関数say()に文字列を渡すと牛のアスキーアートの文字列を返す仕様になっています。以下のコードは、functionノードが受け取ったメッセージ内の変数msg.payloadから牛のアスキーアートの文字列を生成し、msg.payloadに格納した後、次のノードにメッセージとして渡す処理です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287766/a5187696-bc4d-a85d-185c-9aca5c2a0709.png)

**貼り付けたコード**

```JavaScript
msg.payload = cowsay2.say(msg.payload);
return msg;
```

　この作成したfunctionノードの動作を確認するため前後に、文字列を渡すinjectノードと、生成した文字列を確認するdebugノードを接続したフローを作成してみます。

![undeployedflow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287766/f2fb595c-9421-a114-79bf-de50ce939209.png)

　injectノードのプロパティ設定には、変数msg.payloadに文字列「Hello!」を代入する設定を行いました。

![inject.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287766/f9e6ac93-5577-c6d8-cdf9-eb78acc739ad.png)

　フローを作成した後、デブロイボタンをクリックすると、Node-REDのバックエンドのサーバ上で設定したnpmモジュールが自動的にインストールされます。

　最後に、injectノードの左側のボタンをクリックしてみましょう。正しく動作していれば、右側のデバッグタブに生成した文字列が出力されます。文字列の左側の三角マークをクリックすると、吹き出しが「Hello!」となっている牛のアスキーアートが正しく表示されるでしょう。

![hello.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287766/02c30d36-53e7-80b9-7615-ffa59b12ab83.png)

## 牛の表情を変えたアスキーアートを表示する
　cowsay2モジュールには、感情に応じて牛のアスキーアートを変えるオプションが存在します。本オプションを利用し、Node-REDプロジェクト公式の[sentimentノード](https://flows.nodered.org/node/node-red-node-sentiment)による感情分析の結果を基に、牛の顔を変化させてみます。

　ここでは、以下のようにinjectノードを用いて、ポジティブな文章「great work!」と、ネガティブな文章「Too bad...」を渡しています。

![sentimentflow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287766/d6e9d1f4-0533-1c09-61bd-983358dbfb23.png)

　functionノードには以下の様な感情スコアによって、wired(興奮)、dead(死)のモードを切り替えるコードを書いています。

```JavaScript
msg.payload = cowsay2.say(msg.payload, {
    mode: 0 < msg.sentiment.score ? 'wired' : 'dead'
});
return msg;
```

　フローをデプロイした後、「great work!」と書かれたinjectノードのボタンをクリックすると、牛の目が大きくなりポジティブな反応をしてくれました。

![greatwork.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287766/6c5c79ac-8606-1144-7a95-443045c9a409.png)

一方、「Too bad...」と書かれたinjectノードのボタンをクリックすると、悲しい反応をしてくれます。

![toobad.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287766/a0cd55a5-d3da-5a56-10c5-771dbc28af4d.png)

# 最後に
　npmモジュールをfunctionノードから利用する最もシンプルな方法をご説明しました。functionノードのプロパティ設定にある4つのタブのうち、今回は「設定」と「コード」のタブの使い方をご紹介しました。[次の記事では、データベース接続を題材として、「初期化処理」と「終了処理」のタブも活用した使い方](https://qiita.com/kazuhitoyokoi/items/e0441d883c2c970bae5f)をご説明してゆきます。

**フローデータ**

```
[{"id":"ad240a04.48efa8","type":"inject","z":"1369b1f7.49224e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello!","payloadType":"str","x":110,"y":80,"wires":[["9605f0fc.80875"]]},{"id":"af42b081.1b9a1","type":"debug","z":"1369b1f7.49224e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":430,"y":80,"wires":[]},{"id":"15c1ff7e.377e91","type":"sentiment","z":"1369b1f7.49224e","name":"","property":"payload","x":300,"y":180,"wires":[["1b9595a3.7ce32a"]]},{"id":"2051247a.a5290c","type":"inject","z":"1369b1f7.49224e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Too bad...","payloadType":"str","x":140,"y":200,"wires":[["15c1ff7e.377e91"]]},{"id":"15b356d8.5d4489","type":"inject","z":"1369b1f7.49224e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Great work!","payloadType":"str","x":130,"y":160,"wires":[["15c1ff7e.377e91"]]},{"id":"9605f0fc.80875","type":"function","z":"1369b1f7.49224e","name":"","func":"msg.payload = cowsay2.say(msg.payload);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"cowsay2","module":"cowsay2"}],"x":260,"y":80,"wires":[["af42b081.1b9a1"]]},{"id":"1b9595a3.7ce32a","type":"function","z":"1369b1f7.49224e","name":"","func":"msg.payload = cowsay2.say(msg.payload, {\n    mode: 0 < msg.sentiment.score ? 'wired' : 'dead'\n});\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"cowsay2","module":"cowsay2"}],"x":260,"y":260,"wires":[["e197314b.33a31"]]},{"id":"e197314b.33a31","type":"debug","z":"1369b1f7.49224e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":430,"y":260,"wires":[]}]
```

**商標について**

- Node-REDは、米国その他の諸国におけるOpenJS Foundationの登録商標です。
- その他記載の会社名、製品名などは、それぞれの会社の商標もしくは登録商標です。

**参考資料**

- [Node-RED v1.3リリースブログ](https://nodered.jp/blog/2021/04/08/version-1-3-released#function-node-use-of-npm-modules)
- [Lightning Talk: Node-RED Update](https://youtu.be/Xhx3OoaWIaw?t=34)