📝 Node-RED Templateノード 完全ガイド

このガイドでは、Node-REDのTemplateノードについて、初心者の方でも理解できるように詳しく説明します。

📚 1. Templateノードとは?

🤔 「Template」って何?

Templateノードは、テンプレート(ひな形)にデータを埋め込んでテキストを生成するノードです。 日常生活で例えると、年賀状の宛名印刷のようなものです。

📮 年賀状の宛名印刷に例えると:

📦 基本的な動作

データ Template Debug

Templateノードは、Mustache構文というテンプレート言語を使用して、 メッセージのプロパティをテンプレートに埋め込み、新しいテキストを生成します。

🔄 Templateノードの動作イメージ:

📥 入力データ (msg)
{ "topic": "Fruits", "payload": [ {"name": "apple", "price": 100}, {"name": "orange", "price": 80} ] }
📝 テンプレート
# {{topic}}の価格表 {{#payload}} - {{name}}: {{price}}円 {{/payload}}
📤 出力結果
# Fruitsの価格表 - apple: 100円 - orange: 80円

⚙️ 2. Mustache構文の基本

🧔 Mustache(マスタッシュ)とは?

Mustacheは「口ひげ」という意味で、{{ }}の形が口ひげに見えることから名付けられました。 シンプルで覚えやすいテンプレート言語です。

📖 Mustache構文一覧

{{変数名}}
変数の展開
変数の値を出力します。HTMLエスケープされます。
{{{変数名}}}
エスケープなし展開
HTMLタグをそのまま出力したい場合に使用。
{{#配列}} ... {{/配列}}
セクション(繰り返し)
配列の各要素に対して内容を繰り返します。
{{#条件}} ... {{/条件}}
条件分岐(真の場合)
値がtruthy(真)の場合に内容を出力。
{{^条件}} ... {{/条件}}
条件分岐(偽の場合)
値がfalsy(偽)または存在しない場合に内容を出力。
{{! コメント }}
コメント
出力には含まれないコメント。
{{.}}
現在の要素
配列のループ内で現在の要素を参照。

📋 設定項目一覧

設定項目 説明 選択肢
プロパティ 出力先のプロパティ msg.payload(デフォルト)、任意のmsgプロパティ
構文 テンプレートの構文 Mustache / Plain text
形式 エディタの表示形式 Mustache / Plain text / JSON / YAML / HTML / CSS / JavaScript / Markdown
出力形式 出力データの形式 文字列 / Parsed JSON / Parsed YAML
テンプレート 実際のテンプレート内容 自由記述
名前 ノードの表示名 任意の文字列

🎨 出力形式の違い

文字列 Plain text

テンプレートの結果をそのまま文字列として出力

出力: "Hello, World!" 型: string

JSON Parsed JSON

結果をJSONとしてパースしてオブジェクトで出力

出力: {key: "value"} 型: object

YAML Parsed YAML

結果をYAMLとしてパースしてオブジェクトで出力

出力: {key: "value"} 型: object

🎯 3. 実用的な使用パターン

📥 サンプルフローのインポート方法:

  1. 下のサンプルフローJSONをコピー
  2. Node-REDエディタで メニュー → 読み込み を選択
  3. JSONをペーストして「読み込み」をクリック

このサンプルフローには、以下で説明する全パターンの実例が含まれています。

📋 サンプルフロー(クリックで展開)

参照元:NodeREDエディター内サンプルフロー

[ { "id": "328b545581935c25", "type": "tab", "label": "template", "disabled": false, "info": "", "env": [] }, { "id": "eaf91a6b.a55da8", "type": "comment", "z": "328b545581935c25", "name": "1. Advanced mustache example", "info": "Template node can create a string value using Mustache syntax.", "x": 230, "y": 80, "wires": [] }, { "id": "61fbfe34.14a02", "type": "inject", "z": "328b545581935c25", "name": "Price of fruits", "props": [ {"p": "payload"}, {"p": "topic", "vt": "str"} ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "Fruits", "payload": "[{\"name\":\"apple\",\"price\":100},{\"name\":\"orange\",\"price\":80},{\"name\":\"banana\",\"price\":210}]", "payloadType": "json", "x": 230, "y": 140, "wires": [["bf0cb02.d8e4b5"]] }, { "id": "bf0cb02.d8e4b5", "type": "template", "z": "328b545581935c25", "name": "価格リスト", "field": "payload", "fieldType": "msg", "format": "handlebars", "syntax": "mustache", "template": "# Price List of {{topic}}\n\n{{! outputs list of prices }}\n{{#payload}}\n- {{name}}: {{price}}円\n{{/payload}}", "output": "str", "x": 410, "y": 140, "wires": [["153eb0ff.5622df"]] }, { "id": "153eb0ff.5622df", "type": "debug", "z": "328b545581935c25", "name": "文字列出力", "active": true, "tosidebar": true, "console": false, "tostatus": true, "complete": "payload", "targetType": "msg", "x": 590, "y": 140, "wires": [] }, { "id": "fe821493.2e0e28", "type": "comment", "z": "328b545581935c25", "name": "2. Parse result as JSON", "info": "Template node can create JSON output.", "x": 210, "y": 240, "wires": [] }, { "id": "931f94e8.592cd8", "type": "inject", "z": "328b545581935c25", "name": "Hello World", "props": [ {"p": "payload"}, {"p": "topic", "vt": "str"} ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "message", "payload": "Hello, World!", "payloadType": "str", "x": 230, "y": 300, "wires": [["bb2b0dad.b24b5"]] }, { "id": "bb2b0dad.b24b5", "type": "template", "z": "328b545581935c25", "name": "JSON template", "field": "payload", "fieldType": "msg", "format": "json", "syntax": "mustache", "template": "{\n \"key\": \"{{topic}}\",\n \"value\": \"{{payload}}\"\n}", "output": "json", "x": 420, "y": 300, "wires": [["baf2e48.2b97418"]] }, { "id": "baf2e48.2b97418", "type": "debug", "z": "328b545581935c25", "name": "JSON出力", "active": true, "tosidebar": true, "console": false, "tostatus": true, "complete": "payload", "targetType": "msg", "x": 590, "y": 300, "wires": [] }, { "id": "6ad06659.a1e4e8", "type": "comment", "z": "328b545581935c25", "name": "3. Parse result as YAML", "info": "Template node can create YAML output.", "x": 210, "y": 400, "wires": [] }, { "id": "8d6be9a2.c3fa58", "type": "inject", "z": "328b545581935c25", "name": "Hello World", "props": [ {"p": "payload"}, {"p": "topic", "vt": "str"} ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "message", "payload": "Hello, World!", "payloadType": "str", "x": 230, "y": 460, "wires": [["69369c3.4a98164"]] }, { "id": "69369c3.4a98164", "type": "template", "z": "328b545581935c25", "name": "YAML template", "field": "payload", "fieldType": "msg", "format": "yaml", "syntax": "mustache", "template": "key: {{topic}}\nvalue: {{payload}}", "output": "yaml", "x": 420, "y": 460, "wires": [["11fb2934.f5de27"]] }, { "id": "11fb2934.f5de27", "type": "debug", "z": "328b545581935c25", "name": "YAML出力", "active": true, "tosidebar": true, "console": false, "tostatus": true, "complete": "payload", "targetType": "msg", "x": 590, "y": 460, "wires": [] } ]

パターン1: 配列のループ処理(価格リスト)

用途: 配列データをテンプレートで繰り返し展開

商品データ Template
(Mustache)
Debug
📥 入力 (msg.payload)
[ {"name": "apple", "price": 100}, {"name": "orange", "price": 80}, {"name": "banana", "price": 210} ]
📝 テンプレート
# Price List of {{topic}} {{! outputs list of prices }} {{#payload}} - {{name}}: {{price}}円 {{/payload}}
📤 出力結果
# Price List of Fruits - apple: 100円 - orange: 80円 - banana: 210円

📌 ポイント:

パターン2: JSON出力の生成

用途: テンプレートからJSONオブジェクトを生成

データ Template
(JSON出力)
Debug
📥 入力
msg.topic: "message" msg.payload: "Hello, World!"
📝 テンプレート
{ "key": "{{topic}}", "value": "{{payload}}" }
📤 出力(オブジェクト)
{ key: "message", value: "Hello, World!" } 型: object

設定:

出力形式: Parsed JSON ※ 文字列ではなくJavaScriptオブジェクトとして出力 ※ 後続ノードで obj.key のようにアクセス可能

パターン3: YAML出力の生成

用途: テンプレートからYAML形式のオブジェクトを生成

データ Template
(YAML出力)
Debug
📝 テンプレート(YAML形式)
key: {{topic}} value: {{payload}}
📤 出力(オブジェクト)
{ key: "message", value: "Hello, World!" } 型: object

📌 YAMLの利点:

パターン4: HTMLページの生成

用途: HTTPレスポンス用のHTMLページを動的に生成

HTTP In Template
(HTML)
HTTP Response

テンプレート例:

<!DOCTYPE html> <html> <head> <title>{{title}}</title> </head> <body> <h1>{{title}}</h1> <p>現在時刻: {{timestamp}}</p> <ul> {{#items}} <li>{{name}}: {{value}}</li> {{/items}} {{^items}} <li>データがありません</li> {{/items}} </ul> </body> </html>

パターン5: 条件分岐の使用

用途: 値の有無や真偽で出力内容を変える

📝 テンプレート
ステータス: {{#active}}稼働中{{/active}}{{^active}}停止中{{/active}} {{#error}} ⚠️ エラー: {{error}} {{/error}} {{^error}} ✅ 正常に動作しています {{/error}}
📤 出力例(error無し)
ステータス: 稼働中 ✅ 正常に動作しています

📌 条件分岐のポイント:

🏋️ 4. 実践演習

演習1: シンプルな挨拶文初級

📝 課題:

名前を受け取って「こんにちは、〇〇さん!」という挨拶文を生成してください。

🎯 要求仕様:

📊 期待される動作:

💡 ヒント

Injectノードの設定:

  • msg.payload: 文字列 "田中"

Templateノードの設定:

  • テンプレート: こんにちは、{{payload}}さん!
  • 出力形式: 文字列
✅ 解答例フロー
[ { "id": "ex1_inject", "type": "inject", "name": "名前入力", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "田中", "payloadType": "str", "x": 160, "y": 100, "wires": [["ex1_template"]] }, { "id": "ex1_template", "type": "template", "name": "挨拶文", "field": "payload", "fieldType": "msg", "format": "handlebars", "syntax": "mustache", "template": "こんにちは、{{payload}}さん!", "output": "str", "x": 330, "y": 100, "wires": [["ex1_debug"]] }, { "id": "ex1_debug", "type": "debug", "name": "結果", "active": true, "tosidebar": true, "console": false, "tostatus": true, "complete": "payload", "targetType": "msg", "x": 490, "y": 100, "wires": [] } ]

演習2: 商品リストの生成中級

📝 課題:

商品の配列データから、番号付きリストを生成してください。

🎯 要求仕様:

💡 ヒント

Templateノードの設定:

  • {{#payload}} ... {{/payload}} で配列をループ
  • ループ内で {{name}}{{price}} を参照
✅ 解答例フロー
[ { "id": "ex2_inject", "type": "inject", "name": "商品データ", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "[{\"name\":\"りんご\",\"price\":150},{\"name\":\"みかん\",\"price\":100},{\"name\":\"ぶどう\",\"price\":300}]", "payloadType": "json", "x": 170, "y": 100, "wires": [["ex2_template"]] }, { "id": "ex2_template", "type": "template", "name": "リスト生成", "field": "payload", "fieldType": "msg", "format": "handlebars", "syntax": "mustache", "template": "【商品リスト】\n{{#payload}}\n・{{name}}: {{price}}円\n{{/payload}}", "output": "str", "x": 350, "y": 100, "wires": [["ex2_debug"]] }, { "id": "ex2_debug", "type": "debug", "name": "結果", "active": true, "tosidebar": true, "console": false, "tostatus": true, "complete": "payload", "targetType": "msg", "x": 510, "y": 100, "wires": [] } ]

演習3: センサーデータのJSON変換中級

📝 課題:

センサーから受け取ったデータをJSON形式に変換して出力してください。

🎯 要求仕様:

💡 ヒント

Templateノードの設定:

  • 形式: JSON
  • 出力形式: Parsed JSON
  • テンプレートでJSONを構築
✅ 解答例フロー
[ { "id": "ex3_inject", "type": "inject", "name": "センサーデータ", "props": [ {"p": "payload"}, {"p": "topic", "vt": "str"} ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "sensor01", "payload": "25.5", "payloadType": "num", "x": 180, "y": 100, "wires": [["ex3_template"]] }, { "id": "ex3_template", "type": "template", "name": "JSON変換", "field": "payload", "fieldType": "msg", "format": "json", "syntax": "mustache", "template": "{\n \"device\": \"{{topic}}\",\n \"temperature\": {{payload}},\n \"unit\": \"℃\"\n}", "output": "json", "x": 360, "y": 100, "wires": [["ex3_debug"]] }, { "id": "ex3_debug", "type": "debug", "name": "結果", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "x": 510, "y": 100, "wires": [] } ]

演習4: 条件付きHTMLレポート上級

📝 課題:

センサーデータの状態に応じて、異なる表示を行うHTMLレポートを生成してください。

🎯 要求仕様:

💡 ヒント

条件分岐の使い方:

  • {{#status}}...{{/status}}: statusがtruthyの場合
  • {{^status}}...{{/status}}: statusがfalsyの場合
  • 文字列 "active" はtruthyとして扱われる
  • 空配列 [] はfalsyとして扱われる

statusの判定:

  • Changeノードでstatusが"active"かどうかをブール値に変換すると判定しやすい
  • または、statusの値をそのまま表示する方法でも可
✅ 解答例フロー
[ { "id": "ex4_inject", "type": "inject", "name": "センサー状態", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "{\"status\":\"active\",\"temperature\":28,\"alerts\":[\"高温警告\",\"湿度注意\"]}", "payloadType": "json", "x": 180, "y": 100, "wires": [["ex4_change"]] }, { "id": "ex4_change", "type": "change", "name": "状態判定", "rules": [ { "t": "set", "p": "isActive", "pt": "msg", "to": "$$.payload.status = \"active\"", "tot": "jsonata" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 360, "y": 100, "wires": [["ex4_template"]] }, { "id": "ex4_template", "type": "template", "name": "HTMLレポート", "field": "payload", "fieldType": "msg", "format": "html", "syntax": "mustache", "template": "\n\n\n センサーレポート\n \n\n\n

📊 センサーレポート

\n \n
\n {{#isActive}}🟢 稼働中{{/isActive}}\n {{^isActive}}🔴 停止中{{/isActive}}\n
\n \n

温度: {{payload.temperature}}℃

\n \n

アラート

\n {{#payload.alerts.length}}\n
    \n {{#payload.alerts}}\n
  • ⚠️ {{.}}
  • \n {{/payload.alerts}}\n
\n {{/payload.alerts.length}}\n {{^payload.alerts.length}}\n

✅ 異常なし

\n {{/payload.alerts.length}}\n\n", "output": "str", "x": 540, "y": 100, "wires": [["ex4_debug"]] }, { "id": "ex4_debug", "type": "debug", "name": "HTML出力", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 710, "y": 100, "wires": [] } ]

🎓 5. まとめ

Templateノードの重要ポイント

⚠️ よくある間違い

📚 次のステップ

Templateノードをマスターしたら、以下のノードも学んでみましょう:

🔧 6. トラブルシューティング

よくある問題と解決方法

問題 原因 解決方法
変数が展開されない プロパティ名の誤り Debugノードで実際のプロパティ名を確認
配列がループしない セクション構文の誤り {{#array}}...{{/array}} の形式を確認
JSON出力でエラー テンプレートが不正なJSON JSONバリデーターでテンプレートを検証
HTMLタグが表示される エスケープされている {{{変数}}}(三重括弧)を使用
条件分岐が動かない truthy/falsyの誤解 空文字列、0、null、undefinedはfalsy
ネストしたデータにアクセスできない 参照方法の誤り {{object.property}} のドット記法を使用

💡 7. 実務での活用例

ケース1: メール本文の生成

センサーアラートデータ ↓ Templateノード ↓ 件名: [{{severity}}] {{sensor_name}}のアラート 本文: {{sensor_name}}で異常を検知しました。 日時: {{timestamp}} 値: {{value}} {{unit}} 閾値: {{threshold}} {{unit}} {{#details}} 詳細: {{.}} {{/details}} ※ アラートメールの動的生成

ケース2: REST APIレスポンスの整形

HTTP In(GET /api/status) ↓ データ取得処理 ↓ Templateノード(Parsed JSON) { "status": "{{status}}", "timestamp": "{{timestamp}}", "data": { "temperature": {{temp}}, "humidity": {{humid}} } } ↓ HTTP Response ※ 構造化されたAPIレスポンスの生成

ケース3: ダッシュボードHTML生成

複数センサーデータ ↓ Templateノード(HTML) <div class="dashboard"> {{#sensors}} <div class="sensor-card"> <h3>{{name}}</h3> <p class="value">{{value}} {{unit}}</p> <p class="status {{status}}">{{statusText}}</p> </div> {{/sensors}} </div> ↓ HTTP Response ※ 動的なダッシュボードページの生成

ケース4: 設定ファイルの生成

設定パラメータ ↓ Templateノード(YAML出力) server: host: {{host}} port: {{port}} database: connection: {{db_connection}} pool_size: {{pool_size}} logging: level: {{log_level}} {{#log_file}} file: {{log_file}} {{/log_file}} ↓ ファイル書き込み ※ 設定ファイルの動的生成

📖 8. Mustache構文詳細リファレンス

基本構文

構文 説明
{{変数}} 変数展開(HTMLエスケープあり) {{name}} → "田中"
{{{変数}}} 変数展開(HTMLエスケープなし) {{{html}}} → "<b>太字</b>"
{{.}} 現在のコンテキスト 配列ループ内で要素自体を参照
{{obj.prop}} ネストしたプロパティ {{user.name}}

セクション構文

構文 説明 用途
{{#section}}...{{/section}} セクション(真の場合/配列ループ) 条件表示、配列の繰り返し
{{^section}}...{{/section}} 反転セクション(偽の場合) デフォルト表示、空配列時の処理
{{! comment }} コメント 出力に含まれないメモ

truthy/falsyの判定

判定 {{#section}}の動作
true truthy 内容を表示
false falsy 内容を非表示
"文字列" truthy 内容を表示
""(空文字列) falsy 内容を非表示
[1,2,3](配列) truthy 各要素でループ
[](空配列) falsy 内容を非表示
null / undefined falsy 内容を非表示
0 falsy 内容を非表示

🔗 9. 追加リソース


このガイドが役に立ちましたら、実際のプロジェクトで練習してみてください!
Templateノードはテキスト生成の強力なツールです。

参照元:NodeREDエディター内サンプルフロー

🏠