Functionノードの書き方

Functionノードは、受け取ったメッセージに対してJavaScriptコードを実行することができ、 フローを継続するためにゼロ個以上のメッセージを返します。

メッセージはmsgと呼ばれる一つのオブジェクトとして渡されます。 このオブジェクトは慣例により、メッセージ本体を含むmsg.payloadプロパティを持っています。

独自のプロパティをメッセージに追加することも可能ですが、 そのことをドキュメントに記載するべきです。

Functionの作成

Functionノードに入力されたコードは、関数の本体に相当します。 最も単純な関数は、メッセージを単にそのまま返す場合です。:

return msg;

関数がnullを返すと、メッセージは何も渡されずにフローが終了します。

返されるメッセージオブジェクトは受け取ったオブジェクトと同一である必要はありません。 全く新しいオブジェクトを作って返すこともできます。 以下がその例です。:

var newMsg = { payload: msg.payload.length };
return newMsg;
Note : 新しいメッセージオブジェクトを生成すると、 受信した元のメッセージのあらゆるプロパティを失うことになります。これによりフローが壊れることがあり、 例えば HTTP In/Response フローでは msg.reqmsg.res プロパティが 最初から最後まで失われないようにする必要があります。 一般論として、functionノードはどんな変更をプロパティに行っても、 受け取ったメッセージオブジェクトを返すべきです。

複数の出力に送る

Functionノードの編集ダイアログで出力の数を変更することができます。 2つ以上の出力がある場合、複数の出力にメッセージを送るために、 複数のメッセージを含む一つの配列を返すことができます。

これにより、条件に応じて異なる出力にメッセージを送る関数を簡単に書くことができます。 例えば、以下の例はtopicがbananaなら最初の出力ではなく 2番目の出力に全てを送ります。:

if (msg.topic === "banana") {
   return [ null, msg ];
} else {
   return [ msg, null ];
}

以下の例は、入力されたメッセージをそのまま最初の出力に渡し、 そのメッセージのpayloadの長さを含むメッセージを2番目の出力に渡します。:

var newMsg = { payload: msg.payload.length };
return [msg, newMsg];

複数のメッセージを送る

複数メッセージの配列を含む一つの配列を返すことで、 一つの出力に複数のメッセージを返すことができます。 一つの出力に複数のメッセージを返すと、 後続のノードは返された配列の要素の順番にメッセージを一つずつ受け取ります。

以下の例では、msg1msg2msg3は最初の出力に送られます。 msg4は2番目の出力に送られます。

var msg1 = { payload:"first out of output 1" };
var msg2 = { payload:"second out of output 1" };
var msg3 = { payload:"third out of output 1" };
var msg4 = { payload:"only message from output 2" };
return [ [ msg1, msg2, msg3 ], msg4 ];

以下の例は、受信したpayloadを個別の単語に分け、 それぞれの単語を順次送る一つのメッセージを返します。

var outputMsgs = [];
var words = msg.payload.split(" ");
for (var w in words) {
    outputMsgs.push({payload:words[w]});
}
return [ outputMsgs ];

メッセージの非同期送信

メッセージを送信する前に何らかの非同期処理を行う必要がある場合、 関数の最後で(非同期処理の結果の)メッセージを返すことはできません。

その場合、送信したいメッセージ(複数可)を node.send() 関数に渡して用いる必要があります。 以下がその例です。:

doSomeAsyncWork(msg, function(result) {
    node.send({payload:result});
});
return;

関数の中で非同期のコールバックを用いる場合、 フローが再デプロイされる際に処理中のリクエストや使用中のコネクションなどを後片付けする必要があります。 この後片付けは、closeイベントハンドラを追加することで行うことができます。

node.on('close', function() {
    // コネクションの切断など、全ての非同期コードの後片付けをここで行う
});

イベントのログ

ノードが何かをコンソールにログ出力する必要がある場合は、以下の関数のどれかを使うことができます:

node.log("何かが起きました");
node.warn("知っておくべき何かが起きました");
node.error("なんてこった、何か良くないことが起きました");

warnerror メッセージはフローエディタのdebugタブにも送られます。

より詳細なログには、node.trace()およびnode.debug()が利用できます。 これらのレベルを出力するようにログが設定されていない場合、ログには出力されません。

エラーの処理

実行中のフローを停止させるべきエラーに遭遇した場合、関数は何も返却するべきではありません。 同じタブのCatchノードをトリガーするために、 元のメッセージを第2引数としてnode.error を呼ぶべきです。:

node.error("エラー発生", msg);

データの保存

msgオブジェクトとは違って、functionノードは複数回の呼び出しの間で、自分のcontextオブジェクトの中にデータを保持しておくことができます。

Node-REDのコンテキストについての詳細な情報はこちらで確認することができます。

Functionノードの場合、 コンテキストにアクセスすることができる3つの定義済み変数が存在します。:

  • context - ノードスコープのローカルなコンテキスト
  • flow - フロースコープのコンテキスト
  • global - グローバルスコープのコンテキスト

以下の例ではflowコンテキストを利用しますが、 contextおよびglobalでも同様に利用できます。

Note : これらの定義済み変数はFunctionノードの特徴です。 ノードを自作する場合は、contextへのアクセス方法はノード作成ガイドを参照してください。

コンテキストへのアクセスには、同期または非同期の2種類のモードがあります。 既存のコンテキストストアは両方のモードを提供しています。いくつかのストアは非同期アクセスのみ提供しており、 同期してアクセスがおこなわれるとエラーを発生させます。

contextから値を取得するには以下のようにします。:

var myCount = flow.get("count");

値を設定するには以下のようにします。:

flow.set("count", 123);

以下の例は関数が何回実行されているか、 その回数を保持します。:

// もしまだカウンタが存在していなければ0に初期化
var count = context.get('count')||0;
count += 1;
// 値を書き戻して保存
context.set('count',count);
// 送信するメッセージの一部にカウントを含める
msg.count = count;
return msg;
複数の値の取得/設定

Node-RED 0.19以降、複数の値の取得または設定を1回でできるようになっています。:

// Node-RED 0.19以降
var values = flow.get(["count", "colour", "temperature"]);
// values[0]には'count'の値が格納される
// values[1]には'colour'の値が格納される
// values[2]には'temprature'の値が格納される
// Node-RED 0.19以降
flow.set(["count", "colour", "temperature"], [123, "red", "12.5"]);

この方法では、値が見つからない場合にはnullが設定されます。

コンテキストへの非同期なアクセス

コンテキストストアでのデータ保管に非同期なアクセスが必要な場合、 getおよびset関数はコールバック引数が必須です。

// 単一の値を取得
flow.get("count", function(err, myCount) { ... });

// 複数の値を取得
flow.get(["count", "colour"], function(err, count, colour) { ... })

// 単一の値を設定
flow.set("count", 123, function(err) { ... })

// 複数の値を設定
flow.set(["count", "colour", [123, "red"], function(err) { ... })

コールバック関数に渡される第1引数errは、 contextにアクセスする際にエラーが発生する場合のみセットします。

先述のカウンタの例を、非同期におこなうと以下のようになります。:

context.get('count', function(err, count) {
    if (err) {
        node.error(err, msg);
    } else {
        // もしまだカウンタが存在していなければ0に初期化
        count = count || 0;
        count += 1;
        // 値を書き戻して保存
        context.set('count',count, function(err) {
            if (err) {
                node.error(err, msg);
            } else {
                // 送信するメッセージの一部にカウントを含める
                msg.count = count;
                // メッセージの送信
                node.send(msg);
            }
        });
    }
});
複数のコンテキストストア

0.19版では、複数のコンテキストストアに値を格納できます。 例として、memoryおよびfileといったストアの両方を利用することができます。

get/set関数は、利用するストアを特定するため、 任意の引数を利用することができます。

// 値の同期取得
var myCount = flow.get("count", storeName);

// 値の非同期取得
flow.get("count", storeName, function(err, myCount) { ... });

// 値の同期設定
flow.set("count", 123, storeName);

// 値の非同期設定
flow.set("count", 123, storeName, function(err) { ... })
globalコンテキスト

globalコンテキストは、Node-RED起動時にオブジェクトを予め格納しておくことができます。 オブジェクトは、settings.jsファイルのfunctionGlobalContextプロパティで 定義することができます。

このテクニックはFunctionノード内で追加モジュールのロードに 利用することができます。

ステータスの追加

他のノードが行えるのと同じように、functionノードもステータス表示を装飾することができます。 ステータスをセットするには、node.status関数を呼びます。 例えば

node.status({fill:"red",shape:"ring",text:"切断されています"});
node.status({fill:"green",shape:"dot",text:"接続されています"});
node.status({text:"文字だけの状態"});
node.status({});   // 状態をクリアします

指定可能なパラメータの詳細については Nodeのステータスを参照してください。

ステータスのどんな変化でも、Statusノードでキャッチできます。

追加モジュールのロード

Functionノードに追加モジュールを直接ロードすることはできません。 settings.jsファイルでロードし、 functionGlobalContextプロパティに追加される必要があります。

例えば、os組込みモジュールはsettings.jsファイルに以下の設定を追加することによって、 すべてのFunctionノードで利用可能になります。

functionGlobalContext: {
    osModule:require('os')
}

この時点で、モジュールはFunctionノード内で以下のように参照できます。 global.get('osModule')

設定ファイルでロードされたモジュールは、設定ファイルと同じディレクトリにインストールされる必要があります。 デフォルトのユーザディレクトリ(~/.node-red)を利用しているだろうほとんどのユーザは以下のようにインストールを実行します。:

cd ~/.node-red
npm install name_of_3rd_party_module

APIリファレンス

Functionノード内では以下のオブジェクトが利用できます。

node

context

  • context.get(..) : ノードスコープコンテキストからプロパティ値を取得する
  • context.set(..) : ノードスコープコンテキストにプロパティ値を設定する
  • context.keys(..) : すべてのノードスコープコンテキストのプロパティキーの一覧を取得する
  • context.flow : flowと同義です
  • context.global : globalと同義です

flow

  • flow.get(..) : フロースコープコンテキストからプロパティ値を取得する
  • flow.set(..) : フロースコープコンテキストにプロパティ値を設定する
  • flow.keys(..) : すべてのフロースコープコンテキストのプロパティキーの一覧を取得する

global

  • global.get(..) : グローバルスコープコンテキストからプロパティ値を取得する
  • global.set(..) : グローバルスコープコンテキストにプロパティ値を設定する
  • global.keys(..) : すべてのグローバルスコープコンテキストのプロパティキーの一覧を取得する

RED

  • RED.util.cloneMessage(..) : メッセージオブジェクトを再利用できるように安全にクローンします

その他のモジュールと関数

functionノードは以下のモジュールと関数を利用可能です。:

  • Buffer - Node.jsのBufferモジュール
  • console - Node.jsのconsoleモジュール(ログ出力にはnode.logを利用する方が良いです。)
  • util - Node.jsのutilモジュール
  • setTimeout/clearTimeout - javascriptのtimeout関数
  • setInterval/clearInterval - javascriptのinterval関数

Note: functionノードは、停止もしくは再デプロイ時に、 全ての未完了のタイムアウトやインターバルタイマーを自動的にクリアします。