Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 1 year has passed since last update.

目からウロコ!Node-REDのアンチパターン10選

Posted at

 私がNode-REDを使い始めて4年が経ちました。これまでにハッカソンや勉強会等でNode-REDのフローを見てきて、開発の後戻りや混乱が起きやすいアンチパターンが分かってきたため、紹介します。

Node-REDとは

 マウスで部品をつなぐ操作のみでアプリケーションをノンプログラミングで開発できる開発環境です。IoT分野に注力している企業は、ほぼ全て活用しておりIoT業界標準のソフトウェアとなっています。
 Node-REDは、クラウドとエッジデバイスの環境で使えるため、両環境に対応させたフローを開発することでポータピリティ性の高いフローになります。また、Node-REDはクラウドネイティブアプリケーションの開発ルールである12 Factor Appに沿った作りとなっているため、フローにおいても本ルールに従うことで、今時の(?)開発手法の恩恵を受けることができます。

それではさっそく作りがちなアンチパターンと解決方法を紹介します。

1. コーディング能力が必要となるfunctionノードを多用する

 functionノードはJavaScriptを記述できる便利ノードですが、沢山使ってしまうとコーディングになってしまいノンプログラミングで素早く開発できるNode-REDの利点がなくなってしまいます。例えば以下のフロー内では、1つ目のfunctionノード「AbCdを設定」では「msg.payload = 'AbCd';」というコードを記載し、msg.payloadに値を設定しています。2つ目のfunctionノード「大文字を小文字に変換」では、「msg.payload = msg.payload.toLowerCase();」というコードで大文字を小文字に変換しています。

function1.png

 値の代入を行う際は、changeノードかtemplateノードを使いましょう。やむをおえずfunctionノードを用いた場合は、Node generatorを用いて一般的な自作ノードに変換し、フローライブラリに公開すると良いです。これによってコーディングができない他のユーザがノードを再利用できるようになります。

function2.png

 その他、四則演算などの簡単な処理を行いたい場合は、changeノードのJSONata機能を使うとシンプルになります。

2. changeノードで複数の変数を代入する

 前述の様にfunctionノードで値の代入を行う代わりとして、changeノードを用いて代入することがあります。1個の変数の代入であれば、changeノードを用いると簡単ですが、複数の変数を代入しだすと、どの値を扱っているかが分かりにくくなってしまいます。例えば、以下のフローではmsg.payloadに入った「タイムスタンプ」とmsg.topicに入った「topicValue」という値を合わせて、「{"timestamp": "<タイムスタンプ>", "value": "topicValue"}」というJSONオブジェクトを生成しています。

change1.png change3.png

 changeノードのプロパティでは、msg.payloadのタイムスタンプをmsg.tmpに移動した後、新規にJSONオブジェクトに値を代入しています。ルールが3つもあり何をしているか分かりにくいです。
 本問題を解決するには、templateノードのMustacheテンプレートを用いると複数の変数の代入が分かりやすくなります。Mustacheテンプレートでは前のノードから受け取ったJSONデータ内の変数を「{{}}」で指定して新たなJSONオブジェクトを生成できます。

change2.png change4.png

3. injectノードに初期値を定義する

 injectノードのノードプロパティでは、次のノードに渡す値として、タイムスタンプ値以外にも変数やJSONデータを定義できる様になっています。例として以下では、GPIO端子のON/OFFを制御するために0または1をgpioノードに送るフローを用いました。

inject1.png inject4.png

 フローの実行と値設定を同時にできて一見便利そうですが、フローの再利用の観点だと値は設定しない方が良いです。以下の様にinjectノードとtemplateノードに分けてフローを作っておいた方が、後々フローを修正する手間が減ります。

inject2.png

 例えば、後からswitchノードで判定結果を基に0か1を送る様に変更したい場合、templateノードの前にswitchノードを追加するだけでよくなります。もしinjectノードに値を設定していた場合は、後からtemplateノードを追加しないといけないことが分かるかと思います。

inject3.png

4. execノードで環境依存のコマンドを実行する

 execノードはNode-REDから手軽にコマンドを実行できて便利ですが、Mac、Linux、Windows環境間でフローの受け渡しをすると、環境依存のコマンドが実行できないためトラブルが起こります。例えば、Raspberry PiのIPアドレスを取得するために「hostname -i」コマンドをexecノードで実行することがよくあります。

exec1.png

 この書き方をしてしまうと、MacやWindowsで動作しなくなってしまいます。代替手段としてIPアドレスを取得するサードパーティノードを用い、全てのNode.js環境で動作する書き方の方が親切です。

exec2.png

 他のケースにおいても、execノードに環境依存のコマンドを書く前に、他の環境でも動くサードパーティノードがないか探してみましょう。

5. fileノードを用いてデータを保存する

 Node-REDでは「センサから集めたデータをcsv形式でfileノードを用いて保存する」というフローを書きがちですが、データ管理で後から困るケースが多いです。
file1.png

 まずPaaS環境では、インスタンスを再起動するとファイルシステムに保存したデータが消える仕様になっているものが多いため、データを消失してしまいます。そのため、初めからfileノードが使えない様になっているNode-RED環境もあります。クラウドとエッジデバイスの両方で動作するフローにするには、fileノードの代替としてDBノードを用いた方が良いです。Node-RED公式のMySQLノードMongoDBノードがおすすめです(MySQLノードの使い方はこちらが参考になります)。

file2.png

DBを用いることで古いデータがディスク領域を圧迫してきた際に、一部データを削除することも容易になります。

6. フローの編集が困難なsubflowを多用する

 subflowノードは、一連のフローをノード化して再利用するために用いるノードです。プログラミング言語の関数の様に使えますが、一度subflow化してしまうと中のフローは編集が困難です。特にsubflowが入れ子になっている場合は、階層構造が複雑化しバグを作り込む原因となります。

subflow1.png

 subflow化したいフローは、代わりにhttp-in、http responseノードを用いてREST API化し、http requestノードから呼び出すようにしましょう。

subflow2.png

 REST API化の副次的な効果として、一部のフローを切り出して別のNode-REDインスタンスで動作させ、負荷分散や障害に強いマイクロサービスアーキテクチャに変更できるメリットもあります。マイクロサービス向けのテストツールも使えるため、フローの品質維持もできるようになります。

7. ノードの説明をcommentノードに記載する

 ノードの説明を書くために、各ノードの横にcommentノードを配置することがあります。

comment1.png

 このフローを編集する際、ノードの移動と合わせて対応するcommentノードも移動する必要が出てきて、開発の時間の殆どがノードの配置に費やされてしまいます。commentノードはフローの説明だけとし、ノードの説明はノードプロパティの中にある「説明」の中に記載しましょう(v0.20から有効)。

comment2.png comment3.png

 ノードの説明を記載すると、情報タブでノードの説明を確認できます。

comment4.png

 特にhttp-inノードにおいては、HTTPエンドポイントの説明を記載できるnode-red-node-swaggerノードを用いましょう。

swagger1.png swagger3.png

 本機能を用いるとhttp://localhost:1880/http-api/swagger.jsonからOpen API定義を取得できるため、API Managementとの連携したり、Node generatorを用いてREST APIにアクセスする自作ノードを開発したりできるようになります。また、Node-REDフローエディタ上で以下の様なSwagger UIを用いてエンドポイントをテストすることもできます。

swagger2.png

8. フロー内に環境固有の変数を定義する

 Node-RED起動時に初期化処理としてグローバルコンテキストやフローコンテキストに値を入れるフローを作成される方がよくいます。

env1.png

 固定の値なら問題ないですが、環境毎に異なる値を入れる必要がある場合、フローを新環境に読み込む毎に値を記入する必要があります。クラウドネイティブアプリケーションの開発では、一般的に環境変数に環境固有の情報を格納します。Node-REDは環境変数から値を読み取る機能があるため、Node-REDにおいても環境変数に設定した値を用いるようにしましょう。

env2.png

 これによって複数のNode-RED環境を用意する際、環境変数を設定するのみで環境固有の情報を設定できるため、デプロイ作業が簡単になります。

9. 変数名が衝突する可能性があるグローバルコンテキストを使う

 ノード間で値を共有する場合、グローバルコンテキストを用いることがあります。もし2つの異なるフローを1つのNode-REDに読み込んだ場合、グローバルコンテキストの変数名が衝突してしまい、フローの挙動がおかしくなってしまうことがあります。

 全てのタブで共有する必要がある変数以外は、タブ内のノードで共有するフローコンテキストを用いるようにしましょう。

10. デバックタブのみでログを参照する

 Node-REDの開発では、debugノードを用いてデータをデバッグタブに出力し、フローを流れているデータを確認します。この確認方法は開発時の一時的な方法であり、WebSocket接続切断や表示件数超過によってデータがデバッグタブから消えてしまいます。
 ログ管理を行いたい場合は、debugノードのノードプロパティにて「システムコンソール」にチェックを入れます。

debug.png

 これによってデフォルトで標準出力にログを出力できるため、Papertrailの様な標準出力をログとして収集するツールが使えます。また、settings.jsにlogstash等のカスタムロガーを指定することもできます。

最後に

 Node-REDはプロトタイプで用いることが多いことから、その場限りのフローを書きがちです。しかし、今後フローのバージョン管理機能や、クラウドやエッジデバイスにデプロイできるデスクトップ版Node-REDが一般的になり本番適用が進んでくると、環境に依存しない再利用性の高いフローが重要になってくると考えます。みなさんもアンチパターンのノウハウを見つけましたら、ぜひ共有してみてください。

紹介したフロー

1. コーディング能力が必要となるfunctionノードを多用する
[{"id":"7e681e.ebe017e4","type":"function","z":"90d22362.8b36","name":"大文字を小文字に変換","func":"msg.payload = msg.payload.toLowerCase();\nreturn msg;","outputs":1,"noerr":0,"x":460,"y":80,"wires":[["fa25f3dd.f88e3"]]},{"id":"5845e859.fb9718","type":"inject","z":"90d22362.8b36","name":"フローを開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":80,"wires":[["350e5cf1.4612c4"]]},{"id":"fa25f3dd.f88e3","type":"debug","z":"90d22362.8b36","name":"abcdを出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":650,"y":80,"wires":[]},{"id":"7bf67a94.878d44","type":"comment","z":"90d22362.8b36","name":"functionノードを用いて大文字を小文字に変換するフロー","info":"","x":230,"y":40,"wires":[]},{"id":"89a71556.577468","type":"inject","z":"90d22362.8b36","name":"フローを開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":180,"wires":[["bb6c9932.fbb1a8"]]},{"id":"fadc1016.34d27","type":"debug","z":"90d22362.8b36","name":"abcdを出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":650,"y":180,"wires":[]},{"id":"cc6068f6.bb0dc8","type":"comment","z":"90d22362.8b36","name":"changeノードと自作ノードを用いて大文字を小文字に変換するフロー","info":"","x":270,"y":140,"wires":[]},{"id":"57e47ee7.fe108","type":"lowercase","z":"90d22362.8b36","name":"大文字を小文字に変換","x":460,"y":180,"wires":[["fadc1016.34d27"]]},{"id":"350e5cf1.4612c4","type":"function","z":"90d22362.8b36","name":"AbCdを設定","func":"msg.payload = 'AbCd';\nreturn msg;","outputs":1,"noerr":0,"x":270,"y":80,"wires":[["7e681e.ebe017e4"]]},{"id":"bb6c9932.fbb1a8","type":"change","z":"90d22362.8b36","name":"AbCdを設定","rules":[{"t":"set","p":"payload","pt":"msg","to":"AbCd","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":180,"wires":[["57e47ee7.fe108"]]}]
2. changeノードで複数の変数を代入する
[{"id":"53341283.27208c","type":"template","z":"30a94c90.74b054","name":"値を代入しJSON作成","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n    \"timestamp\": \"{{payload}}\",\n    \"value\": \"{{topic}}\"\n}","output":"json","x":360,"y":180,"wires":[["d0da7f3f.8dc38"]]},{"id":"83c4c1e5.1d691","type":"change","z":"30a94c90.74b054","name":"値を代入しJSON作成","rules":[{"t":"move","p":"payload","pt":"msg","to":"tmp","tot":"msg"},{"t":"set","p":"payload.timestamp","pt":"msg","to":"tmp","tot":"msg"},{"t":"set","p":"payload.value","pt":"msg","to":"topic","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":80,"wires":[["76501293.3bc43c"]]},{"id":"8d594214.31879","type":"inject","z":"30a94c90.74b054","name":"","topic":"topicValue","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":180,"wires":[["53341283.27208c"]]},{"id":"d0da7f3f.8dc38","type":"debug","z":"30a94c90.74b054","name":"JSONを出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":550,"y":180,"wires":[]},{"id":"26019d5c.1e7822","type":"inject","z":"30a94c90.74b054","name":"","topic":"topicValue","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":80,"wires":[["83c4c1e5.1d691"]]},{"id":"76501293.3bc43c","type":"debug","z":"30a94c90.74b054","name":"JSONを出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":550,"y":80,"wires":[]},{"id":"2c8f7f0.e6a3882","type":"comment","z":"30a94c90.74b054","name":"msg.payloadとmsg.topicからJSONオブジェクトを作成するフロー","info":"","x":260,"y":40,"wires":[]},{"id":"8b174c4e.73c1f","type":"comment","z":"30a94c90.74b054","name":"msg.payloadとmsg.topicからJSONオブジェクトを作成するフロー","info":"","x":260,"y":140,"wires":[]}]
3. injectノードに初期値を定義する
[{"id":"ae977e0e.62161","type":"template","z":"62e57f99.8d1d8","name":"0を設定","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"0","output":"str","x":260,"y":220,"wires":[["70513569.f1143c"]]},{"id":"70513569.f1143c","type":"rpi-gpio out","z":"62e57f99.8d1d8","name":"GPIO端子をON/OFF","pin":"3","set":"","level":"0","freq":"","out":"out","x":460,"y":240,"wires":[]},{"id":"fd18c78b.6bca08","type":"template","z":"62e57f99.8d1d8","name":"1を設定","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"1","output":"str","x":260,"y":260,"wires":[["70513569.f1143c"]]},{"id":"3c7b77df.49ff08","type":"rpi-gpio out","z":"62e57f99.8d1d8","name":"GPIO端子をON/OFF","pin":"3","set":"","level":"0","freq":"","out":"out","x":400,"y":100,"wires":[]},{"id":"19bb1f5c.c57b31","type":"template","z":"62e57f99.8d1d8","name":"0を設定","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"0","output":"str","x":400,"y":360,"wires":[["660fc5ef.d067ec"]]},{"id":"660fc5ef.d067ec","type":"rpi-gpio out","z":"62e57f99.8d1d8","name":"GPIO端子をON/OFF","pin":"3","set":"","level":"0","freq":"","out":"out","x":600,"y":380,"wires":[]},{"id":"33ee0fa.10cc2f","type":"template","z":"62e57f99.8d1d8","name":"1を設定","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"1","output":"str","x":400,"y":400,"wires":[["660fc5ef.d067ec"]]},{"id":"4be6565e.629d48","type":"switch","z":"62e57f99.8d1d8","name":"payload < 40","property":"payload","propertyType":"msg","rules":[{"t":"lt","v":"40","vt":"num"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":230,"y":380,"wires":[["19bb1f5c.c57b31"],["33ee0fa.10cc2f"]]},{"id":"2fab2acf.b7c4f6","type":"mqtt in","z":"62e57f99.8d1d8","name":"温度を取得","topic":"topic","qos":"2","datatype":"auto","broker":"2acb6291.9186ee","x":80,"y":380,"wires":[["4be6565e.629d48"]]},{"id":"b4bf3da2.3026f","type":"inject","z":"62e57f99.8d1d8","name":"フローを開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":220,"wires":[["ae977e0e.62161"]]},{"id":"e0521be8.ad7e18","type":"inject","z":"62e57f99.8d1d8","name":"フローを開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":260,"wires":[["fd18c78b.6bca08"]]},{"id":"21098a71.78bbe6","type":"inject","z":"62e57f99.8d1d8","name":"0を設定後、フローを開始","topic":"","payload":"0","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":80,"wires":[["3c7b77df.49ff08"]]},{"id":"7b813b89.28f324","type":"inject","z":"62e57f99.8d1d8","name":"1を設定後、フローを開始","topic":"","payload":"1","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":120,"wires":[["3c7b77df.49ff08"]]},{"id":"279c1fe7.c68c2","type":"comment","z":"62e57f99.8d1d8","name":"GPIO端子のON/OFFを制御するフロー","info":"","x":170,"y":40,"wires":[]},{"id":"bd22335a.b4bb1","type":"comment","z":"62e57f99.8d1d8","name":"GPIO端子のON/OFFを制御するフロー","info":"","x":170,"y":180,"wires":[]},{"id":"181a08d8.857727","type":"comment","z":"62e57f99.8d1d8","name":"温度が閾値を超えた際、GPIO端子のON/OFFを制御するフロー","info":"","x":250,"y":320,"wires":[]},{"id":"2acb6291.9186ee","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]
4. execノードで環境依存のコマンドを実行する
[{"id":"780ec9ac.825628","type":"exec","z":"2d22483a.83eb98","command":"hostname -i","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":270,"y":80,"wires":[["cd20895c.35faf8"],[],[]]},{"id":"1a8b45cd.85e38a","type":"hostip","z":"2d22483a.83eb98","name":"Host IP","x":260,"y":180,"wires":[["fc1f89a0.d90c18"]]},{"id":"6bd6550f.62dcac","type":"inject","z":"2d22483a.83eb98","name":"フローを開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":80,"wires":[["780ec9ac.825628"]]},{"id":"cd20895c.35faf8","type":"debug","z":"2d22483a.83eb98","name":"IPアドレスを出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":450,"y":80,"wires":[]},{"id":"5e5345f3.b179dc","type":"inject","z":"2d22483a.83eb98","name":"フローを開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":180,"wires":[["1a8b45cd.85e38a"]]},{"id":"fc1f89a0.d90c18","type":"debug","z":"2d22483a.83eb98","name":"IPアドレスを出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":430,"y":180,"wires":[]},{"id":"1b8f35a6.28b6ca","type":"comment","z":"2d22483a.83eb98","name":"IPアドレスを取得するフロー","info":"","x":140,"y":40,"wires":[]},{"id":"82cf837.e69a28","type":"comment","z":"2d22483a.83eb98","name":"IPアドレスを取得するフロー","info":"","x":140,"y":140,"wires":[]}]
5. fileノードを用いてデータを保存する
[{"id":"86074697.cba528","type":"mysql","z":"b9c94991.617d18","mydb":"","name":"MySQLを問合せ","x":540,"y":240,"wires":[["57b97dd3.49f3a4"]]},{"id":"4697c37f.fc8d1c","type":"file","z":"b9c94991.617d18","name":"ファイル書込","filename":"","appendNewline":true,"createDir":false,"overwriteFile":"false","x":500,"y":80,"wires":[[]]},{"id":"335ddc16.c37384","type":"file in","z":"b9c94991.617d18","name":"ファイル読込","filename":"","format":"utf8","chunk":false,"sendError":false,"x":320,"y":120,"wires":[["b9afd63b.a3c9e8"]]},{"id":"57b97dd3.49f3a4","type":"debug","z":"b9c94991.617d18","name":"問合せ結果","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":710,"y":240,"wires":[]},{"id":"70bbdfd3.d2062","type":"template","z":"b9c94991.617d18","name":"INSERT文を作成","field":"topic","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"insert into users(name) values('{{name}}');","output":"str","x":330,"y":220,"wires":[["86074697.cba528"]]},{"id":"19735350.0a14bd","type":"inject","z":"b9c94991.617d18","name":"データ書込を実行","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":220,"wires":[["70bbdfd3.d2062"]]},{"id":"e9bb092a.600fa8","type":"inject","z":"b9c94991.617d18","name":"データ読取を実行","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":260,"wires":[["cbeb3af8.fcfc08"]]},{"id":"cbeb3af8.fcfc08","type":"template","z":"b9c94991.617d18","name":"SELECT文を作成","field":"topic","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"SELECT * FROM users;","output":"str","x":330,"y":260,"wires":[["86074697.cba528"]]},{"id":"b9afd63b.a3c9e8","type":"debug","z":"b9c94991.617d18","name":"読取データを出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":510,"y":120,"wires":[]},{"id":"ca5fb5ca.162948","type":"template","z":"b9c94991.617d18","name":"書き込むデータ","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"This is the payload: {{payload}} !","output":"str","x":320,"y":80,"wires":[["4697c37f.fc8d1c"]]},{"id":"621c207f.24fa3","type":"inject","z":"b9c94991.617d18","name":"データ書込を実行","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":80,"wires":[["ca5fb5ca.162948"]]},{"id":"b07de0d4.84337","type":"inject","z":"b9c94991.617d18","name":"データ読取を実行","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":120,"wires":[["335ddc16.c37384"]]},{"id":"62d07d49.3c3e44","type":"comment","z":"b9c94991.617d18","name":"fileノードを用いてデータの書込/読取を行うフロー","info":"","x":210,"y":40,"wires":[]},{"id":"e7d140a3.e0eb9","type":"comment","z":"b9c94991.617d18","name":"MySQLノードを用いてデータの書込/読取を行うフロー","info":"","x":220,"y":180,"wires":[]}]
6. フローの編集が困難なsubflowを多用する
[{"id":"e9e3706e.93005","type":"subflow","name":"Subflow 2","info":"","in":[{"x":40,"y":40,"wires":[{"id":"97a863c6.f60dc"}]}],"out":[{"x":240,"y":40,"wires":[{"id":"97a863c6.f60dc","port":0}]}]},{"id":"97a863c6.f60dc","type":"template","z":"e9e3706e.93005","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"subflow2","output":"str","x":140,"y":40,"wires":[[]]},{"id":"f0b4d6e9.27f1c8","type":"subflow","name":"Subflow 1","info":"","in":[{"x":40,"y":40,"wires":[{"id":"66b1cf38.cd42f"}]}],"out":[{"x":240,"y":40,"wires":[{"id":"66b1cf38.cd42f","port":0}]}]},{"id":"66b1cf38.cd42f","type":"template","z":"f0b4d6e9.27f1c8","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"subflow1","output":"str","x":140,"y":40,"wires":[[]]},{"id":"2fddb1e9.fd897e","type":"http request","z":"ca729981.099898","name":"api1にアクセス","method":"GET","ret":"txt","url":"localhost/api1","tls":"","proxy":"","x":280,"y":180,"wires":[["34b7d724.4278e8"]]},{"id":"90336304.8d357","type":"subflow:f0b4d6e9.27f1c8","z":"ca729981.099898","name":"","x":260,"y":80,"wires":[["c11510c3.4566c"]]},{"id":"4d2ad27d.05b77c","type":"inject","z":"ca729981.099898","name":"フローを開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":80,"wires":[["90336304.8d357"]]},{"id":"bd872f1f.19a5a","type":"http in","z":"ca729981.099898","name":"","url":"/api1","method":"get","upload":false,"swaggerDoc":"","x":100,"y":220,"wires":[["c2198059.3187c"]]},{"id":"6b1453da.6d379c","type":"inject","z":"ca729981.099898","name":"フローを開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":180,"wires":[["2fddb1e9.fd897e"]]},{"id":"6977a929.f4ce78","type":"debug","z":"ca729981.099898","name":"出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":610,"y":180,"wires":[]},{"id":"228c27ca.0d61f8","type":"http response","z":"ca729981.099898","name":"","statusCode":"","headers":{},"x":390,"y":220,"wires":[]},{"id":"d4c31410.46fc08","type":"http in","z":"ca729981.099898","name":"","url":"/api2","method":"get","upload":false,"swaggerDoc":"","x":100,"y":260,"wires":[["69eba473.f6b02c"]]},{"id":"947f902e.00932","type":"http response","z":"ca729981.099898","name":"","statusCode":"","headers":{},"x":390,"y":260,"wires":[]},{"id":"34b7d724.4278e8","type":"http request","z":"ca729981.099898","name":"api2にアクセス","method":"GET","ret":"txt","url":"localhost/api2","tls":"","proxy":"","x":460,"y":180,"wires":[["6977a929.f4ce78"]]},{"id":"4f1a8ebb.a0e79","type":"debug","z":"ca729981.099898","name":"出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":530,"y":80,"wires":[]},{"id":"c2198059.3187c","type":"template","z":"ca729981.099898","name":"api1の処理","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"api1","output":"str","x":250,"y":220,"wires":[["228c27ca.0d61f8"]]},{"id":"69eba473.f6b02c","type":"template","z":"ca729981.099898","name":"api2の処理","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"api2","output":"str","x":250,"y":260,"wires":[["947f902e.00932"]]},{"id":"b06bee6a.f0209","type":"comment","z":"ca729981.099898","name":"サブフローを用いたフロー","info":"","x":130,"y":40,"wires":[]},{"id":"a733d6fb.3ba158","type":"comment","z":"ca729981.099898","name":"サブフローの代わりにREST APIを用いたフロー","info":"","x":200,"y":140,"wires":[]},{"id":"c11510c3.4566c","type":"subflow:e9e3706e.93005","z":"ca729981.099898","name":"","x":400,"y":80,"wires":[["4f1a8ebb.a0e79"]]}]
7. ノードの説明をcommentノードに記載する
[{"id":"312b4c60.4fe5e4","type":"comment","z":"445f5678.c13758","name":"↑getFullYear()を用いて西暦を取得","info":"","x":310,"y":120,"wires":[]},{"id":"8721e41.e68d918","type":"function","z":"445f5678.c13758","name":"西暦を取得","func":"msg.payload = new Date(msg.payload).getFullYear();\nreturn msg;","outputs":1,"noerr":0,"x":250,"y":80,"wires":[["57f61a12.99ede4"]]},{"id":"187627a.db90cd8","type":"inject","z":"445f5678.c13758","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":100,"y":80,"wires":[["8721e41.e68d918"]]},{"id":"4cb77667.82f3b8","type":"debug","z":"445f5678.c13758","name":"平成の年を出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":620,"y":80,"wires":[]},{"id":"4cf9c44b.85d2ac","type":"comment","z":"445f5678.c13758","name":"↓西暦から1988を引くことで平成の年を算出","info":"","x":530,"y":40,"wires":[]},{"id":"bd6506d8.f254f8","type":"comment","z":"445f5678.c13758","name":"平成の年を出力するフロー","info":"","x":130,"y":40,"wires":[]},{"id":"c5b6dbef.b2da88","type":"function","z":"445f5678.c13758","name":"西暦を取得","func":"msg.payload = new Date(msg.payload).getFullYear();\nreturn msg;","outputs":1,"noerr":0,"x":250,"y":220,"wires":[["9f9dd077.df2b4"]],"info":"getFullYear()を用いて西暦を取得"},{"id":"fe175309.d2ccb","type":"inject","z":"445f5678.c13758","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":100,"y":220,"wires":[["c5b6dbef.b2da88"]]},{"id":"806e6782.e138e8","type":"debug","z":"445f5678.c13758","name":"平成の年を出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":620,"y":220,"wires":[]},{"id":"df36af43.81b2c","type":"comment","z":"445f5678.c13758","name":"平成の年を出力するフロー","info":"","x":130,"y":180,"wires":[]},{"id":"57f61a12.99ede4","type":"change","z":"445f5678.c13758","name":"西暦を平成に変換","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload-1988","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":80,"wires":[["4cb77667.82f3b8"]]},{"id":"9f9dd077.df2b4","type":"change","z":"445f5678.c13758","name":"西暦を平成に変換","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload-1988","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":220,"wires":[["806e6782.e138e8"]],"info":"西暦から1988を引くことで平成の年を算出"},{"id":"e68bac15.21894","type":"http in","z":"445f5678.c13758","name":"","url":"/api","method":"get","upload":false,"swaggerDoc":"16c1b9de.ece046","x":80,"y":320,"wires":[["8d50fdee.4ad8c"]]},{"id":"9d3778e6.c2aeb8","type":"http response","z":"445f5678.c13758","name":"","statusCode":"","headers":{},"x":350,"y":320,"wires":[]},{"id":"8d50fdee.4ad8c","type":"template","z":"445f5678.c13758","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"This is the payload: {{payload}} !","output":"str","x":220,"y":320,"wires":[["9d3778e6.c2aeb8"]]},{"id":"297757e7.a1a728","type":"comment","z":"445f5678.c13758","name":"REST APIのフロー","info":"","x":110,"y":280,"wires":[]},{"id":"16c1b9de.ece046","type":"swagger-doc","z":"","summary":"REST APIのサマリ","description":"REST APIの詳細","tags":"","consumes":"","produces":"","parameters":[],"responses":{},"deprecated":false}]
8. フロー内に環境固有の変数を定義する
[{"id":"7e55c77a.175908","type":"inject","z":"4e944cf6.7632f4","name":"フローを開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":340,"wires":[["b2263a5d.e1e3a8"]]},{"id":"b2263a5d.e1e3a8","type":"change","z":"4e944cf6.7632f4","name":"payload = $foo","rules":[{"t":"set","p":"payload","pt":"msg","to":"foo","tot":"env"}],"action":"","property":"","from":"","to":"","reg":false,"x":280,"y":340,"wires":[["3411f322.7701dc"]]},{"id":"3411f322.7701dc","type":"debug","z":"4e944cf6.7632f4","name":"fooValueを出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":460,"y":340,"wires":[]},{"id":"b2b60731.d404f8","type":"inject","z":"4e944cf6.7632f4","name":"フローを開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":80,"wires":[["d555ec27.3eaf4"]]},{"id":"d555ec27.3eaf4","type":"change","z":"4e944cf6.7632f4","name":"global.foo = 'fooValue'","rules":[{"t":"set","p":"foo","pt":"global","to":"fooValue","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":300,"y":80,"wires":[[]]},{"id":"2424e39e.e7285c","type":"inject","z":"4e944cf6.7632f4","name":"フローを開始","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":180,"wires":[["144b4e9b.8bebb1"]]},{"id":"144b4e9b.8bebb1","type":"change","z":"4e944cf6.7632f4","name":"payload = global.foo","rules":[{"t":"set","p":"payload","pt":"msg","to":"foo","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":300,"y":180,"wires":[["4c1c8d37.182f34"]]},{"id":"4c1c8d37.182f34","type":"debug","z":"4e944cf6.7632f4","name":"fooValueを出力","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":500,"y":180,"wires":[]},{"id":"769267.771c1d98","type":"comment","z":"4e944cf6.7632f4","name":"(2) 環境変数から値を取得","info":"","x":130,"y":300,"wires":[]},{"id":"33eb4a97.89f826","type":"comment","z":"4e944cf6.7632f4","name":"(2) グローバルコンテキストから値を取得","info":"","x":180,"y":140,"wires":[]},{"id":"fd2ff6df.f92fa8","type":"comment","z":"4e944cf6.7632f4","name":"(1) Node-RED起動前に環境変数へ値を設定(export foo=fooValueを実行)","info":"","x":270,"y":260,"wires":[]},{"id":"373c73d6.25360c","type":"comment","z":"4e944cf6.7632f4","name":"(1) グローバルコンテキストに値を設定","info":"","x":170,"y":40,"wires":[]}]
0

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?