⚡ Node-RED Functionノード ガイド

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

📚 1. Functionノードとは?

🤔 「Function」って何?

Functionノードは、JavaScriptコードを書いてメッセージを自由に加工できるノードです。 日常生活で例えると、料理人のようなものです。

👨‍🍳 料理人に例えると:

📦 基本的な動作

Inject Function Debug

Functionノードは、入力メッセージを受け取り、JavaScriptコードで処理して、結果を出力します。 他のノードでは実現できない複雑な処理を実装できます。

⚙️ 2. Functionノードの設定

🔧 設定タブの構成

Setupタブ

ノードが起動するときに1回だけ実行されるコード。初期化処理に使用。

  • 変数の初期化
  • 外部リソースへの接続
  • 非同期処理も可能(Promiseを返す)

Functionタブ

メッセージを受け取るたびに実行されるメインのコード

  • msg.payloadの加工
  • 条件分岐
  • データ変換

Closeタブ

ノードが停止するときに1回だけ実行されるコード。クリーンアップ処理に使用。

  • リソースの解放
  • ログ出力
  • 状態の保存

📋 基本設定項目

設定項目 説明
名前 ノードの表示名 "データ変換", "カウンター"
出力数 出力ポートの数 1〜複数(条件分岐時に増やす)
初期化処理 初期化コード context.set('count', 0);
コード メイン処理コード msg.payload = ...;
終了処理 終了処理コード console.log("終了");
タイムアウト 実行タイムアウト(ミリ秒、0=無制限) 0(デフォルト)
モジュール 使用する外部モジュールのリスト [](設定で許可が必要)

🎯 利用可能な組み込みオブジェクト

msg メッセージオブジェクト

入力メッセージを参照・変更

msg.payload // データ本体 msg.topic // トピック名 msg._msgid // メッセージID

context ノードコンテキスト

ノード内でデータを永続化

context.get('key') context.set('key', value)

flow フローコンテキスト

同じフロー内で共有

flow.get('key') flow.set('key', value)

global グローバルコンテキスト

全フローで共有

global.get('key') global.set('key', value)

node ノードオブジェクト

ノード制御・ログ出力

node.send(msg) node.log("情報") node.warn("警告") node.error("エラー", msg) node.status({...})

env 環境変数

環境変数へのアクセス

env.get('MY_VAR')

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

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

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

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

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

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

[ { "id": "98aa28d4a248e8cb", "type": "tab", "label": "function", "disabled": false, "info": "", "env": [] }, { "id": "ec5a531b.68b65", "type": "inject", "z": "98aa28d4a248e8cb", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "World", "payloadType": "str", "x": 290, "y": 140, "wires": [ [ "961abba6.04a028" ] ] }, { "id": "1b0f8c3e.1fd7e4", "type": "debug", "z": "98aa28d4a248e8cb", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 670, "y": 140, "wires": [] }, { "id": "4e5bf6b2.b4dd58", "type": "comment", "z": "98aa28d4a248e8cb", "name": "send a message to output port", "info": "Function node can be used to write JavaScript code to handle messages.\nThe input message can be referrenced by `msg` variable. \nA message returned from body of the function is sent to output port.\n\nSee Node-RED user guide about [functions](https://nodered.org/docs/user-guide/writing-functions).", "x": 310, "y": 80, "wires": [] }, { "id": "961abba6.04a028", "type": "function", "z": "98aa28d4a248e8cb", "name": "return a message", "func": "// returning message send it to output port\nmsg.payload = \"Hello, \"+msg.payload +\"!\";\nreturn msg;", "outputs": 1, "timeout": "", "initialize": "", "finalize": "", "libs": [], "x": 470, "y": 140, "wires": [ [ "1b0f8c3e.1fd7e4" ] ] }, { "id": "c2b3b0f1.62189", "type": "inject", "z": "98aa28d4a248e8cb", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 300, "y": 280, "wires": [ [ "7241db1e.8946c4" ] ] }, { "id": "c6191361.0f3c", "type": "debug", "z": "98aa28d4a248e8cb", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 730, "y": 260, "wires": [] }, { "id": "3aa5727c.e5019e", "type": "comment", "z": "98aa28d4a248e8cb", "name": "send multiple message", "info": "Function node can send multiple messages to output ports by returning an array of messages. \n\nSee Node-RED user guide about [functions](https://nodered.org/docs/user-guide/writing-functions).", "x": 280, "y": 220, "wires": [] }, { "id": "7241db1e.8946c4", "type": "function", "z": "98aa28d4a248e8cb", "name": "return array of messages", "func": "// returning array of message send elements to output ports\nvar msg1 = { payload:\"first out of output 1\" };\nvar msg2 = { payload:\"second out of output 1\" };\nvar msg3 = { payload:\"third out of output 1\" };\nvar msg4 = { payload:\"only message from output 2\" };\nreturn [ [ msg1, msg2, msg3 ], msg4 ];", "outputs": 2, "timeout": "", "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 280, "wires": [ [ "c6191361.0f3c" ], [ "23a53d00.c89b74" ] ] }, { "id": "23a53d00.c89b74", "type": "debug", "z": "98aa28d4a248e8cb", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 730, "y": 300, "wires": [] }, { "id": "cc74476e.cbdf68", "type": "inject", "z": "98aa28d4a248e8cb", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "Hello, World!", "payloadType": "str", "x": 310, "y": 420, "wires": [ [ "7d90286.706e9d8" ] ] }, { "id": "909906c3.ea9f58", "type": "debug", "z": "98aa28d4a248e8cb", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 770, "y": 420, "wires": [] }, { "id": "d5fc0512.4cd9c8", "type": "comment", "z": "98aa28d4a248e8cb", "name": "sending messages asynchronously", "info": "Function node can asynchronously send a messages to output ports by calling `node.send` function.\n\nSee Node-RED user guide about [functions](https://nodered.org/docs/user-guide/writing-functions).", "x": 320, "y": 360, "wires": [] }, { "id": "7d90286.706e9d8", "type": "function", "z": "98aa28d4a248e8cb", "name": "wait 2s then send message", "func": "// setTimeout calls calls specified callback function asynchronously after a specified time\nsetTimeout(function () {\n node.send(msg);\n}, 2*1000);\n", "outputs": 1, "timeout": "", "initialize": "", "finalize": "", "libs": [], "x": 540, "y": 420, "wires": [ [ "909906c3.ea9f58" ] ] }, { "id": "89c17d21.15da2", "type": "inject", "z": "98aa28d4a248e8cb", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 300, "y": 660, "wires": [ [ "1bcca7af.619428" ] ] }, { "id": "3b9cd70e.8e66e8", "type": "debug", "z": "98aa28d4a248e8cb", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 650, "y": 660, "wires": [] }, { "id": "f76ea68a.6f3c58", "type": "comment", "z": "98aa28d4a248e8cb", "name": "handling errors", "info": "Calling `node.error` function with the original message as a second argument, function node can trigger a catch node in the same tab.\n\nSee Node-RED user guide about [functions](https://nodered.org/docs/user-guide/writing-functions).", "x": 260, "y": 600, "wires": [] }, { "id": "1bcca7af.619428", "type": "function", "z": "98aa28d4a248e8cb", "name": "report error", "func": "// In function node, calling node.error functions with the original input message as its second argument triggers catch node\n// See debug sidebar and console output\nnode.error(\"Oh no, something bad happened\", msg);\n// execution should stops here", "outputs": 1, "timeout": "", "initialize": "", "finalize": "", "libs": [], "x": 470, "y": 660, "wires": [ [ "3b9cd70e.8e66e8" ] ] }, { "id": "74854950.d99558", "type": "catch", "z": "98aa28d4a248e8cb", "name": "", "scope": [ "1bcca7af.619428" ], "uncaught": false, "x": 450, "y": 720, "wires": [ [ "32743f74.e718a" ] ] }, { "id": "32743f74.e718a", "type": "debug", "z": "98aa28d4a248e8cb", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 630, "y": 720, "wires": [] }, { "id": "fb884166.e42f3", "type": "comment", "z": "98aa28d4a248e8cb", "name": "↑ error information can be found in msg.error", "info": "", "x": 750, "y": 760, "wires": [] }, { "id": "64470964.a291a8", "type": "inject", "z": "98aa28d4a248e8cb", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 300, "y": 540, "wires": [ [ "ee4f5964.5cdae8" ] ] }, { "id": "ed028c2f.924b", "type": "debug", "z": "98aa28d4a248e8cb", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 650, "y": 540, "wires": [] }, { "id": "b1686e70.a9f6d", "type": "comment", "z": "98aa28d4a248e8cb", "name": "logging events", "info": "In body of function node code, following logging functions can be used to log events:\n- `node.log`\n- `node.warn`\n- `node.error`\n- `node.trace`\n- `node.debug`\n\nSee Node-RED user guide about [functions](https://nodered.org/docs/user-guide/writing-functions).", "x": 260, "y": 480, "wires": [] }, { "id": "ee4f5964.5cdae8", "type": "function", "z": "98aa28d4a248e8cb", "name": "log events", "func": "// In function node, node.log, node.warn, and node.error functions can be used for logging\n// See debug sidebar and console output\nnode.log(\"Something happened\");\nnode.warn(\"Something happened you should know about\");\nnode.error(\"Oh no, something bad happened\");\nreturn msg;", "outputs": 1, "timeout": "", "initialize": "", "finalize": "", "libs": [], "x": 470, "y": 540, "wires": [ [ "ed028c2f.924b" ] ] }, { "id": "a3a635eb.191188", "type": "inject", "z": "98aa28d4a248e8cb", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 300, "y": 860, "wires": [ [ "77f57d92.06fa14" ] ] }, { "id": "1a218bea.61ae04", "type": "debug", "z": "98aa28d4a248e8cb", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 630, "y": 860, "wires": [] }, { "id": "a56bde3a.330e1", "type": "comment", "z": "98aa28d4a248e8cb", "name": "storing data in context", "info": "The function node code can store data in the context store.\n\nSee Node-RED user guide about [functions](https://nodered.org/docs/user-guide/writing-functions) and [context store](https://nodered.org/docs/user-guide/context).", "x": 280, "y": 800, "wires": [] }, { "id": "77f57d92.06fa14", "type": "function", "z": "98aa28d4a248e8cb", "name": "counter", "func": "// initialise the counter to 0 if it doesn't exist already\nvar count = context.get('count')||0;\ncount += 1;\n// store the value back\ncontext.set('count',count);\n// make it part of the outgoing msg object\nmsg.payload = count;\nreturn msg;", "outputs": 1, "timeout": "", "initialize": "", "finalize": "", "libs": [], "x": 460, "y": 860, "wires": [ [ "1a218bea.61ae04" ] ] }, { "id": "ce0b7cca.34817", "type": "inject", "z": "98aa28d4a248e8cb", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "red", "payloadType": "str", "x": 290, "y": 1000, "wires": [ [ "a4a6c205.8afd4" ] ] }, { "id": "c98c52d0.ab51f", "type": "comment", "z": "98aa28d4a248e8cb", "name": "showing status information", "info": "The function node code can provide status decoration by calling `node.status` function.\n\nSee Node-RED user guide about [functions](https://nodered.org/docs/user-guide/writing-functions).", "x": 290, "y": 940, "wires": [] }, { "id": "a4a6c205.8afd4", "type": "function", "z": "98aa28d4a248e8cb", "name": "show status", "func": "// calling node.status show status information below the function node\nswitch (msg.payload) {\n case \"red\":\n node.status({fill:\"red\",shape:\"ring\",text:\"disconnected\"});\n break;\n case \"green\":\n node.status({fill:\"green\",shape:\"dot\",text:\"connected\"});\n break;\n case \"text\":\n node.status({text:\"Just text status\"});\n break;\n case \"clear\":\n node.status({}); // to clear the status \n break;\n}\n\n", "outputs": 0, "timeout": "", "initialize": "", "finalize": "", "libs": [], "x": 470, "y": 1000, "wires": [] }, { "id": "9f29ae74.8dd11", "type": "inject", "z": "98aa28d4a248e8cb", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "green", "payloadType": "str", "x": 290, "y": 1040, "wires": [ [ "a4a6c205.8afd4" ] ] }, { "id": "83fc1404.ec0b98", "type": "inject", "z": "98aa28d4a248e8cb", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "text", "payloadType": "str", "x": 290, "y": 1080, "wires": [ [ "a4a6c205.8afd4" ] ] }, { "id": "517a869c.0ceab8", "type": "inject", "z": "98aa28d4a248e8cb", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "clear", "payloadType": "str", "x": 290, "y": 1120, "wires": [ [ "a4a6c205.8afd4" ] ] }, { "id": "3ece1efd.306c22", "type": "inject", "z": "98aa28d4a248e8cb", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 380, "y": 1240, "wires": [ [ "c9100743.673598" ] ] }, { "id": "ed3da95d.62ee98", "type": "debug", "z": "98aa28d4a248e8cb", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 750, "y": 1240, "wires": [] }, { "id": "85b5b8ad.0717d8", "type": "comment", "z": "98aa28d4a248e8cb", "name": "using external modules (needs change in settings.js)", "info": "The function node can use external node modules using global context value.\n\nAdditional node modules must be loaded in `settings.js` file and added to the `functionGlobalContext` property. \n\nFor example, following definition in `settings.js` load `os` module on start up of Node-RED. \n\n```\nfunctionGlobalContext: {\n os: require(\"os\")\n}\n```\n\nThe `os` module can be accessed in a function blocks as:\n```\nglobal.get(\"os\")\n```\n\nSee Node-RED user guide about [functions](https://nodered.org/docs/user-guide/writing-functions) and [context store](https://nodered.org/docs/user-guide/context).", "x": 520, "y": 1180, "wires": [] }, { "id": "c9100743.673598", "type": "function", "z": "98aa28d4a248e8cb", "name": "use os module", "func": "// use external os module to access OS information\nvar os = global.get(\"os\");\nmsg.payload = { \n hostname: os.hostname(),\n arch: os.arch(),\n platform: os.platform(),\n release: os.release(),\n free: os.freemem()\n};\nreturn msg;", "outputs": 1, "timeout": "", "initialize": "", "finalize": "", "libs": [], "x": 560, "y": 1240, "wires": [ [ "ed3da95d.62ee98" ] ] }, { "id": "e397736b.a96af", "type": "inject", "z": "98aa28d4a248e8cb", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 300, "y": 1380, "wires": [ [ "900ad01c.561f" ] ] }, { "id": "1e2a78d6.33f617", "type": "debug", "z": "98aa28d4a248e8cb", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 630, "y": 1380, "wires": [] }, { "id": "83d3f2fd.593b4", "type": "comment", "z": "98aa28d4a248e8cb", "name": "setup and close", "info": "The function node code provide setup and closecode. \nThe `Setup` tab contains code that will be run whenever the node is started. The `Close` tab contains code that will be run when the node is stopped.\n\nSee Node-RED user guide about [functions](https://nodered.org/docs/user-guide/writing-functions).", "x": 260, "y": 1320, "wires": [] }, { "id": "900ad01c.561f", "type": "function", "z": "98aa28d4a248e8cb", "name": "counter", "func": "// get value of global counter\nvar count = global.get('count');\ncount += 1;\n// store the value back\nglobal.set('count',count);\n// make it part of the outgoing msg object\nmsg.payload = count;\nreturn msg;\n", "outputs": 1, "timeout": "", "initialize": "// initialize global counter\nglobal.set('count', 0);", "finalize": "// report current counter to console\nvar count = global.get('count');\nconsole.log(\"count:\", count);", "libs": [], "x": 460, "y": 1380, "wires": [ [ "1e2a78d6.33f617" ] ] }, { "id": "89904b80.4fcd08", "type": "inject", "z": "98aa28d4a248e8cb", "d": true, "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 310, "y": 1520, "wires": [ [ "22c235a0.acb41a" ] ] }, { "id": "20fa70dc.7107f", "type": "debug", "z": "98aa28d4a248e8cb", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 670, "y": 1520, "wires": [] }, { "id": "cddf88c9.9df018", "type": "comment", "z": "98aa28d4a248e8cb", "name": "asynchronous setup", "info": "The `Setup` code that performs asynchronous work can return a promise object of the work. The completion of the promise is waited before starting function body.\n\nSee Node-RED user guide about [functions](https://nodered.org/docs/user-guide/writing-functions).", "x": 270, "y": 1460, "wires": [] }, { "id": "22c235a0.acb41a", "type": "function", "z": "98aa28d4a248e8cb", "name": "async. setup", "func": "// retrieve message value\nmsg.payload = global.get('message');\nreturn msg;", "outputs": 1, "timeout": "", "initialize": "// set initial value of message\nglobal.set(\"message\", \"Initializing, World!\");\n// create promise for async work\nvar promise = new Promise((resolve, reject) => {\n // async work: wait 1s, then set message\n setTimeout(() => {\n global.set(\"message\", \"Hello, World!\");\n // report this work successfuly ended\n resolve();\n }, 1000);\n});\n// return the promise that should be executed before function code\nreturn promise;", "finalize": "", "libs": [], "x": 490, "y": 1520, "wires": [ [ "20fa70dc.7107f" ] ] } ]

パターン1: 基本的なメッセージ加工

用途: 入力メッセージを加工して出力する最も基本的なパターン

Inject Function Debug

📌 動作の流れ:

  1. Injectノードからメッセージを受信
  2. Functionノードで msg.payload を加工
  3. return msg; で次のノードに送信

コード例:

// 入力値に挨拶を追加する msg.payload = "Hello, " + msg.payload + "!"; return msg;

パターン2: 複数出力への振り分け

用途: 条件に応じて異なる出力ポートにメッセージを送信

Inject Function
(2出力)
Debug 1

Debug 2

📌 戻り値の形式:

コード例:

// 数値が50以上なら出力1、未満なら出力2に振り分け if (msg.payload >= 50) { return [msg, null]; } else { return [null, msg]; }

パターン3: 複数メッセージの送信

用途: 1つの入力から複数のメッセージを生成

Inject
(1回)
Function →→→ Debug
(3回受信)

📌 戻り値の形式:

コード例:

// 3つのメッセージを順次送信 var msg1 = { payload: "first" }; var msg2 = { payload: "second" }; var msg3 = { payload: "third" }; return [[msg1, msg2, msg3]];

パターン4: 非同期処理

用途: setTimeout、API呼び出し等の非同期処理後にメッセージを送信

Inject Function
(2秒待機)
⋯→ Debug

📌 動作の流れ:

  1. メッセージを受信
  2. setTimeout() で遅延実行を設定
  3. return は不要(何も返さない)
  4. 処理完了後、node.send(msg) でメッセージを送信

コード例:

// 2秒後にメッセージを送信 setTimeout(function() { node.send(msg); }, 2000); // 注意: 非同期処理では return しない

パターン5: コンテキストでデータを保存

用途: メッセージ間でデータを保持する(カウンター、状態管理など)

Inject
(複数回)
Function
(カウンター)
Debug
(1, 2, 3...)

📌 コンテキストの種類:

コード例:

// カウンター: 0から始めて1ずつ増加 var count = context.get('count') || 0; count += 1; context.set('count', count); msg.payload = count; return msg;

パターン6: ステータス表示

用途: ノードの下に状態を表示して可視化

Inject Function
● 接続中
Debug

📌 node.status() のオプション:

コード例:

// 接続状態をステータスで表示 if (msg.payload === "connected") { node.status({ fill: "green", shape: "dot", text: "接続中" }); } else { node.status({ fill: "red", shape: "ring", text: "切断" }); }

パターン7: エラーハンドリング

用途: エラーを適切に処理し、Catchノードで捕捉する

Inject Function Debug

Catch Error Debug

📌 エラー報告の種類:

※ 第2引数にmsgを渡すとCatchノードが受け取れる

コード例:

// 入力値の検証とエラー処理 if (typeof msg.payload !== "number") { node.error("数値ではありません", msg); return null; } msg.payload = msg.payload * 2; return msg;

💡 ヒント: ログ出力関数の使い分け

関数 用途 デバッグサイドバー
node.log() 一般的な情報 表示されない
node.warn() 警告 表示される
node.error() エラー 表示される
node.trace() 詳細なトレース 設定による
node.debug() デバッグ情報 設定による

🏋️ 4. 実践演習

演習1: 基本的なメッセージ加工初級

📝 課題:

名前を入力すると「Hello, [名前]!」と挨拶メッセージを生成するフローを作成してください。

🎯 要求仕様:

📊 期待される動作:

✅ 成功の条件:

💡 ヒント

Functionノードの基本構造:

  • msg.payloadで入力値を取得
  • 文字列結合は + 演算子を使用
  • return msg; で結果を出力
✅ 解答例フロー
[ { "id": "ex1_inject", "type": "inject", "name": "名前入力", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "World", "payloadType": "str", "x": 120, "y": 100, "wires": [["ex1_function"]] }, { "id": "ex1_function", "type": "function", "name": "挨拶生成", "func": "// 入力値に挨拶を追加する\nmsg.payload = \"Hello, \" + msg.payload + \"!\";\nreturn msg;", "outputs": 1, "timeout": "", "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 100, "wires": [["ex1_debug"]] }, { "id": "ex1_debug", "type": "debug", "name": "結果", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 450, "y": 100, "wires": [] } ]

演習2: カウンター機能中級

📝 課題:

ボタンを押すたびにカウントが1ずつ増加するカウンターを作成してください。

🎯 要求仕様:

📊 期待される動作:

✅ 成功の条件:

💡 ヒント

contextの使い方:

  • context.get('count')で値を取得
  • 値がない場合のデフォルト: || 0
  • context.set('count', value)で値を保存
✅ 解答例フロー
[ { "id": "ex2_inject", "type": "inject", "name": "カウントアップ", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 130, "y": 100, "wires": [["ex2_function"]] }, { "id": "ex2_function", "type": "function", "name": "カウンター", "func": "// 現在のカウント値を取得(未定義なら0)\nvar count = context.get('count') || 0;\n\n// カウントを1増加\ncount += 1;\n\n// 値を保存\ncontext.set('count', count);\n\n// 出力\nmsg.payload = count;\nreturn msg;", "outputs": 1, "timeout": "", "initialize": "", "finalize": "", "libs": [], "x": 310, "y": 100, "wires": [["ex2_debug"]] }, { "id": "ex2_debug", "type": "debug", "name": "カウント値", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 490, "y": 100, "wires": [] } ]

演習3: 条件分岐とステータス表示中級

📝 課題:

温度データを受け取り、30度以上なら「高温」、10度以下なら「低温」、それ以外は「適温」と判定し、ノードにステータスを表示してください。

🎯 要求仕様:

📊 期待される動作:

✅ 成功の条件:

💡 ヒント

node.status()のオプション:

  • fill: "red", "blue", "green"
  • shape: "dot"(塗りつぶし)または"ring"(輪)
  • text: 表示するテキスト
✅ 解答例フロー
[ { "id": "ex3_inject_hot", "type": "inject", "name": "35度(高温)", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "35", "payloadType": "num", "x": 130, "y": 80, "wires": [["ex3_function"]] }, { "id": "ex3_inject_normal", "type": "inject", "name": "25度(適温)", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "25", "payloadType": "num", "x": 130, "y": 120, "wires": [["ex3_function"]] }, { "id": "ex3_inject_cold", "type": "inject", "name": "5度(低温)", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "5", "payloadType": "num", "x": 130, "y": 160, "wires": [["ex3_function"]] }, { "id": "ex3_function", "type": "function", "name": "温度判定", "func": "// 温度を取得\nvar temp = msg.payload;\nvar status = {};\n\n// 温度に応じてステータスを設定\nif (temp >= 30) {\n status = {\n fill: \"red\",\n shape: \"dot\",\n text: \"高温: \" + temp + \"℃\"\n };\n msg.payload = \"高温\";\n} else if (temp <= 10) {\n status = {\n fill: \"blue\",\n shape: \"dot\",\n text: \"低温: \" + temp + \"℃\"\n };\n msg.payload = \"低温\";\n} else {\n status = {\n fill: \"green\",\n shape: \"dot\",\n text: \"適温: \" + temp + \"℃\"\n };\n msg.payload = \"適温\";\n}\n\n// ステータスを表示\nnode.status(status);\n\nreturn msg;", "outputs": 1, "timeout": "", "initialize": "", "finalize": "", "libs": [], "x": 330, "y": 120, "wires": [["ex3_debug"]] }, { "id": "ex3_debug", "type": "debug", "name": "判定結果", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 510, "y": 120, "wires": [] } ]

演習4: 非同期処理とエラーハンドリング上級

📝 課題:

センサーデータを受け取り、2秒後にデータを処理して送信するシステムを作成してください。ただし、データが不正な場合はエラーを発生させ、Catchノードで捕捉してください。

🎯 要求仕様:

📊 期待される動作:

✅ 成功の条件:

💡 ヒント

非同期処理とエラーの組み合わせ:

  • typeof msg.payload === "number"で型チェック
  • setTimeout()で遅延処理
  • node.error("message", msg)でCatchノードに送信
  • エラー時はreturn null;で処理を終了
✅ 解答例フロー
[ { "id": "ex4_inject_num", "type": "inject", "name": "数値(50)", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "50", "payloadType": "num", "x": 120, "y": 80, "wires": [["ex4_function"]] }, { "id": "ex4_inject_str", "type": "inject", "name": "文字列(error)", "props": [{"p": "payload"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "error", "payloadType": "str", "x": 130, "y": 120, "wires": [["ex4_function"]] }, { "id": "ex4_function", "type": "function", "name": "非同期処理", "func": "// 入力値の型チェック\nif (typeof msg.payload !== \"number\") {\n node.error(\"数値ではありません: \" + msg.payload, msg);\n return null;\n}\n\n// 2秒後に処理を実行\nvar value = msg.payload;\nsetTimeout(function() {\n msg.payload = value * 2;\n node.send(msg);\n}, 2000);\n\n// 非同期処理なのでreturnしない", "outputs": 1, "timeout": "", "initialize": "", "finalize": "", "libs": [], "x": 330, "y": 100, "wires": [["ex4_debug"]] }, { "id": "ex4_debug", "type": "debug", "name": "処理結果", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 530, "y": 80, "wires": [] }, { "id": "ex4_catch", "type": "catch", "name": "エラー捕捉", "scope": ["ex4_function"], "uncaught": false, "x": 330, "y": 160, "wires": [["ex4_error_debug"]] }, { "id": "ex4_error_debug", "type": "debug", "name": "エラー情報", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "x": 530, "y": 160, "wires": [] } ]

🎓 5. まとめ

Functionノードの重要ポイント

⚠️ よくある間違い

📚 次のステップ

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

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

よくある問題と解決方法

問題 原因 解決方法
メッセージが出力されない return文がない コードの最後にreturn msg;を追加
非同期処理後に出力されない returnを使っている node.send(msg);に変更
カウンターがリセットされる ローカル変数を使用 context.set/getを使用
エラーがCatchで捕捉されない 第2引数がない node.error("msg", msg)と記述
JSONのパースエラー 文字列がJSON形式でない try-catchで囲む
外部モジュールが使えない settings.jsの設定不足 functionGlobalContextに追加

💡 7. 実務での活用例

ケース1: センサーデータの変換

// 温度センサーの生データを摂氏に変換 var rawValue = msg.payload; var celsius = (rawValue - 32) * 5 / 9; msg.payload = { raw: rawValue, celsius: celsius.toFixed(2), timestamp: new Date().toISOString() }; return msg;

ケース2: データの集約

// 複数のセンサー値を集約 var data = flow.get('sensorData') || []; data.push(msg.payload); // 最新10件のみ保持 if (data.length > 10) { data.shift(); } flow.set('sensorData', data); // 平均値を計算 var sum = data.reduce(function(a, b) { return a + b; }, 0); msg.payload = { average: sum / data.length, count: data.length }; return msg;

ケース3: APIレスポンスの整形

// APIレスポンスから必要なデータを抽出 try { var response = msg.payload; msg.payload = { id: response.data.id, name: response.data.name, status: response.data.active ? "有効" : "無効" }; node.status({fill:"green", shape:"dot", text:"成功"}); return msg; } catch (e) { node.error("パースエラー: " + e.message, msg); node.status({fill:"red", shape:"ring", text:"エラー"}); return null; }

ケース4: メッセージのフィルタリング

// 重複メッセージを除外 var lastValue = context.get('lastValue'); if (msg.payload === lastValue) { // 同じ値なら送信しない return null; } // 新しい値を保存 context.set('lastValue', msg.payload); return msg;

📗 8. 追加リソース


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

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

🏠