Node-REDでハマりがちな罠

[Node-RED](http://nodered.org/)でハマりがちな罠をまとめます。

## 1. Debugの内容が途切れる
人にNode-REDを薦めると最初に言われるのがコレ。

ほとんどの場合、プロパティを絞り込んでデバッグするのでデフォルトの文字数で問題ないのですが、用途によってはどうしてもメッセージ全体を確認しないといけないような場合もあります。

その場合はDebugに表示できる文字数を増やしましょう。Debugに表示できる文字数は`settings.js`の`debugMaxLength`の値を増やすだけです。

## 2. データ型
Debugで処理対象のデータがJSONに見えたとしても、それはObjectでなくString（文字列）かもしれません。

以下は、あるMQTTブローカをSubscribeしているflowですがDebugをよく見るとStringと表示されてます。
![mqtt_node.png](https://qiita-image-store.s3.amazonaws.com/0/21109/9f95b34f-baaf-da95-641c-7fb2a0333d50.png)

これをこのままswitch nodeで条件分岐しようとすると、以下のような「プロパティがない」というエラーが表示されます。
![switch_node.png](https://qiita-image-store.s3.amazonaws.com/0/21109/b36d45f8-db56-aada-2b21-91f3b9f29d30.png)

ここでは条件の対象として`payload.d.id`というプロパティを指定していますが、対象データがStringのままなので何を指定してもダメです。まずは対象のデータ型をObjectへ変換します。

JSON StringをObjectに変換するにはfunction nodeで`JSON.parse()`しても良いのですが同様のことはJSON nodeで可能です。

以下のようにするとMQTTブローカから取得したJSON Stringを一旦JSON Obejctへ変換してから条件分岐へ流すようにできます。
![json_node.png](https://qiita-image-store.s3.amazonaws.com/0/21109/facc6d19-4094-9a50-2c2b-f1107d03ae21.png)

## 3. HTTPレスポンス
http in nodeでNode-REDにEndpointを作成してWebサーバとして処理を待ち受けるというようなユースケースはよくあります。

たとえば以下のように`[get]/red/test`というEndpointを作成してアクセスが来たらtemplate nodeに指定したWebページを表示します（Webページは「テスト」という文字だけ表示されるようになっています）
![http_in_node.png](https://qiita-image-store.s3.amazonaws.com/0/21109/5d1e8706-ab63-09e9-3a0e-025ad4327840.png)

しかし実際に`/red/test`にアクセスしてみるとタイムアウトします（Node-REDが動作している環境によって挙動は変わります）
![template_node.png](https://qiita-image-store.s3.amazonaws.com/0/21109/268b433d-fe8b-bc68-1f55-39e3ceeb1671.png)

これは、先ほどのflowのhttp in nodeでHTTPリクエストを受け付けたのにHTTPレスポンスを返す処理を設定していないため、クライアント（ブラウザ）が延々とHTTPレスポンスを待ち続けている状態になっているからです。

以下のようにhttp out nodeを設定して正しくHTTP通信を実装してください。
![http_out_node.png](https://qiita-image-store.s3.amazonaws.com/0/21109/754ef222-d334-7c70-d408-73915d324c32.png)
![htt_out_node.png](https://qiita-image-store.s3.amazonaws.com/0/21109/455a832d-9eee-a7ec-53f9-365119ce15d8.png)

ちなみにhttp out nodeの位置にも気をつけましょう。flowのどこかに配置すれば一応HTTP通信が成立して動いているようには見えますが、flowで一連の処理をした後の値をクライアントへ返したいような場合は、その処理の後続としてhttp out nodeを配置するようにしましょう。

## 4. flowの冗長化
同じような動作をするflowを幾つも並べていませんか？以下は先ほどのWebページへアクセスするflowをNode-REDサイトだけでなくGoogleとYahooに対しても行うようにコピーしただけです。
![redundancy.png](https://qiita-image-store.s3.amazonaws.com/0/21109/953f56cf-1a90-f34c-5e67-4ee98e972559.png)

もちろんコレでも良いのですが、アクセス先が増えたりflowが複雑になってくると破綻してきます。flowの共通部分が何かを意識して差異を動的な値として1本のflowに与えていくような設計にしてみましょう。

たとえば上記flowの場合はアクセス先以外はすべて共通なので、Node-RED, Google, YahooのURLを動的なパラメータとして渡せるようにしてみます。

http request nodeのinfoを見るとアクセスするURLは`msg.url`として動的に値を渡すことができます（他にもmethodやheaderも渡すことができると書いてます）
![http_in_node_info.png](https://qiita-image-store.s3.amazonaws.com/0/21109/0f72771e-4d82-c952-be25-2024a6a9a2ba.png)

ということでアクセス先のURLを配列として保持して以下のようにloopさせることで1本の処理に動的なパラメータを渡して処理するflowになります。
![loop.png](https://qiita-image-store.s3.amazonaws.com/0/21109/d6beb2ca-2d20-a4d4-18fb-534fc322af08.png)

以下をインポートするとサンプルフローを再現できます。

```json:
[{"id":"1d42eedf.e2bd11","type":"http request","name":"","method":"GET","ret":"txt","url":"","x":157,"y":150,"z":"96d4a95.f692b58","wires":[["e8e419f0.171be8"]]},{"id":"a0f49389.5f0b7","type":"inject","name":"","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":145,"y":88,"z":"96d4a95.f692b58","wires":[["eb81bea6.147e4"]]},{"id":"e8e419f0.171be8","type":"html","tag":"title","ret":"html","as":"single","name":"","x":318,"y":150,"z":"96d4a95.f692b58","wires":[["963ff6aa.69c008"]]},{"id":"963ff6aa.69c008","type":"debug","name":"","active":true,"console":"false","complete":"payload","x":477,"y":150,"z":"96d4a95.f692b58","wires":[]},{"id":"6c9c4de.f9363b4","type":"function","name":"","func":"msg.url = msg.payload;\nreturn msg;","outputs":1,"x":498,"y":97,"z":"96d4a95.f692b58","wires":[["1d42eedf.e2bd11"]]},{"id":"eb81bea6.147e4","type":"function","name":"for each item","func":"if( msg.i     == undefined ) msg.i = 0;\nif( msg.items == undefined ) {\n\tmsg.items = [\n\t\t\"http://nodered.org/\",\n\t\t\"http://google.com/\",\n\t\t\"http://yahoo.com/\"\n\t];\n}\n\nmsg.payload = msg.items[ msg.i ];\n\nreturn msg;","outputs":1,"x":322,"y":92,"z":"96d4a95.f692b58","wires":[["794b0123.86b5","6c9c4de.f9363b4"]]},{"id":"794b0123.86b5","type":"function","name":"++","func":"if ( (msg.i += 1) < msg.items.length ) {\n\treturn msg;\n}","outputs":1,"x":323,"y":44,"z":"96d4a95.f692b58","wires":[["eb81bea6.147e4"]]}]
```

もちろんアクセス先が数千とか数万箇所になってくれば、配列ではなくDBやファイルに永続化してflowからアクセス先情報を完全に分離しましょう。

また、このように**後続のnodeに動的な値を渡すことがNode-RED活用のコツの一つ**だと思います。データベース系のnodeはSQLなどデータベース操作をするスクリプトを前段のnodeから渡すようにinfoに書かれていることがほとんどです。

## 5. プロパティ値の上書き
先ほどのWebアクセスのflowでhttp request nodeのURLを指定したまま外部から動的に値を渡すと以下のようなエラーが表示されます。
![override.png](https://qiita-image-store.s3.amazonaws.com/0/21109/2d63bcd9-343f-0ebe-bf5b-d79aee2aa05d.png)

これはメッセージ内に表記されているリンク先（ http://bit.ly/nr-override-msg-props ）の通り、nodeに設定した固定の値を上書きすることで予期しない結果になる事象があるため、file系nodeやhttp系nodeでプロパティの上書きを推奨しないというものです。

特にfunction nodeで以下のように `msg` を返すような書き方が散見されますが、これは `msg` オブジェクトの他のプロパティを無意識に消滅させてしまいます。Node-REDのnodeは `msg` オブジェクトの受け渡しを前提に動作している場合が多いので以下の書き方をしてしまうと思わぬハマりに遭遇してしまいます。

```javascript
retuen msg = {
  payload: "hoge"
};
```

## 6. template nodeでのプロパティアクセス

template nodeでデータをバインドしてWebページとして返したり、メールの本文として整形したりといったユースケースはよくあります。

この時のプロパティアクセスは必ず`.`（ドット）で辿ります。`['title']`などブラケットでは辿れません。

![template_node.png](https://qiita-image-store.s3.amazonaws.com/0/21109/b022812d-a70d-4531-f88a-ebf98ff3d9dd.png)

上記のようなObjectを流した場合はハマらないのですが、配列を流した時に慣習的に`[0]`と書いてしまいがちです。

## 7. あるnodeの状態によって後続のflowを実行したい

例えば、以下のようにメール送信するflowがあったとします。ただ、email nodeの後続に端子がないので後続のflowを作りたくても作れません。

<img width="521" alt="スクリーンショット 2018-08-03 11.58.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/21109/dc7685a0-982a-e3d9-59e4-cf094fa8e7dd.png">

こういう場合は、以下のようにstatus nodeを置いてemail nodeの状態を監視します。

<img width="572" alt="スクリーンショット 2018-08-03 11.59.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/21109/1286aed7-362f-c5e7-fcf8-758897863bfb.png">

こうすることでメール送信してみると、以下のようにメール送信時とメール送信完了時の2回、email nodeの状態がdebug nodeに通知されますので、あとはswitch nodeなどで `status.text` の内容で後続の処理を分岐するような流れになります（ステータスメッセージはnodeの作り次第になりますのでemail nodeの送信完了時のように何もメッセージがないというような場合も往々にしてあります...）

<img width="971" alt="スクリーンショット 2018-08-03 12.01.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/21109/043403d1-8e84-5e0b-24d7-4b368c2016bf.png">

## 8. 日本語のプロパティにアクセスする方法

template node や change node で日本語のプロパティにアクセスする場合は以下のようにします。

### template node

![スクリーンショット 2023-03-07 17.38.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21109/cdc8739c-d8cc-5e4f-9348-03dd32d75f9d.png)

### change node

![スクリーンショット 2023-03-07 17.39.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21109/224fee60-80f1-1684-e781-fd802925b4c4.png)

## 9. template node で文字化けする

以下の画像のURLのところですが `{{` だと文字参照として表示されてしまいます。

![スクリーンショット 2023-03-07 17.54.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21109/0587094f-0f85-cee9-ef92-da05d7bfa6f0.png)

```html
<img src="https:&#x2F;&#x2F;d1d7kfcb5oumx0.cloudfront.net&#x2F;articles&#x2F;images&#x2F;5b30b3cee08d441789edad89&#x2F;thumb_0.jpg" />
<ul>
    <li>タイトル: 生後9ヶ月メス1匹</li>
    <li>都道府県: 東京</li>
    <li>所在地: 中野区 新中野駅 猫</li>
    <li>本文: 先住の犬と相性が悪くて泣く泣く里親に出すことにしました メス白黒のパンダがいます ！早急にお願いします ワクチン 去勢はしてないです</li>
</ul>
<hr />
```

以下の画像のように `{{{` で囲むと文字参照として表示されません。

![スクリーンショット 2023-03-07 17.55.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21109/537f6e79-50ab-4224-37cc-166c14794f29.png)

```html
<img src="https://d1d7kfcb5oumx0.cloudfront.net/articles/images/5b30b3cee08d441789edad89/thumb_0.jpg" />
<ul>
    <li>タイトル: 生後9ヶ月メス1匹</li>
    <li>都道府県: 東京</li>
    <li>所在地: 中野区 新中野駅 猫</li>
    <li>本文: 先住の犬と相性が悪くて泣く泣く里親に出すことにしました メス白黒のパンダがいます ！早急にお願いします ワクチン 去勢はしてないです</li>
</ul>
<hr />
```

## 10. 動的なキーでオブジェクトにアクセスしたい

javascriptで書くと以下のようなアクセスのことです。

```javascript
let obj = {
  a: "田中",
  b: "山田",
}

const key = "a";

console.log(obj[key]); // => 田中
```

change node でも以下のように書けます。

![スクリーンショット 2023-03-07 17.59.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21109/7691deba-5eb5-9c81-b682-13e1e0b9dfb0.png)


以上、今後もハマり次第こちらに追加していきたいと思います。

## 最後に: Node-REDについてさらに知りたい

[Node-RED User Group Japan](https://www.facebook.com/groups/noderedjp/) へどうぞ！