Low-code programming platform for event-driven applications with visual flow-based editor and runtime system
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
JavaScript execution environment and APIs available within Node-RED Function nodes. Function nodes provide a secure sandbox for running custom JavaScript code with access to Node-RED's messaging system and context storage.
Global objects and APIs available in the Function node execution environment.
/**
* Available in Function node context
*/
interface FunctionNodeGlobals {
/** Current message being processed */
msg: NodeMessage;
/** Node instance with control methods */
node: NodeInstance;
/** Node-scoped persistent storage */
context: ContextStore;
/** Flow-scoped persistent storage */
flow: ContextStore;
/** Global-scoped persistent storage */
global: ContextStore;
/** Environment variables access */
env: EnvironmentVariables;
/** Node-RED utilities subset */
RED: NodeREDUtilities;
}
/**
* Message object structure
*/
interface NodeMessage {
/** Unique message identifier */
_msgid: string;
/** Message topic (optional) */
topic?: string;
/** Message payload (any type) */
payload: any;
/** Additional message properties */
[key: string]: any;
}Basic Function Node Example:
// Simple message transformation
msg.payload = msg.payload.toString().toUpperCase();
msg.timestamp = Date.now();
return msg;
// Multiple outputs
if (msg.payload > 10) {
return [msg, null]; // Send to first output only
} else {
return [null, msg]; // Send to second output only
}
// Multiple messages to same output
return [
[
{ payload: "First message" },
{ payload: "Second message" }
],
null
];Methods available on the node object for controlling node behavior and output.
/**
* Node instance methods
*/
interface NodeInstance {
send(msg: NodeMessage | NodeMessage[], clone?: boolean): void;
error(err: string | Error, msg?: NodeMessage): void;
warn(warning: string | object): void;
log(info: string | object): void;
status(status: NodeStatus | string | boolean | number): void;
done(err?: Error): void;
}
/**
* Send message(s) to connected nodes
* @param msg - Message or array of messages to send
* @param clone - Whether to clone messages before sending (default: true)
*/
node.send(msg: NodeMessage | NodeMessage[], clone?: boolean): void;
/**
* Log error and trigger catch nodes
* @param err - Error message or Error object
* @param msg - Optional message that caused the error
*/
node.error(err: string | Error, msg?: NodeMessage): void;
/**
* Log warning message
* @param warning - Warning message or object
*/
node.warn(warning: string | object): void;
/**
* Log informational message
* @param info - Info message or object
*/
node.log(info: string | object): void;
/**
* Set node status indicator
* @param status - Status object, string, boolean, or number
*/
node.status(status: NodeStatus | string | boolean | number): void;Usage Examples:
// Send processed message
msg.payload = processData(msg.payload);
node.send(msg);
// Send without cloning (more efficient but potentially unsafe)
node.send(msg, false);
// Error handling
try {
msg.payload = JSON.parse(msg.payload);
node.send(msg);
} catch (err) {
node.error("Invalid JSON in payload", msg);
}
// Status updates
node.status({ fill: "blue", shape: "ring", text: "processing..." });
// Clear status
node.status({});
// Logging
node.log(`Processing message with topic: ${msg.topic}`);
node.warn("Deprecated feature used");Persistent storage system available at node, flow, and global scopes.
/**
* Context storage interface
*/
interface ContextStore {
get(key: string, callback?: (err: Error, value: any) => void): any;
get(keys: string[], callback?: (err: Error, values: any[]) => void): any[];
set(key: string, value: any, callback?: (err: Error) => void): void;
set(keys: string[], values: any[], callback?: (err: Error) => void): void;
keys(callback?: (err: Error, keys: string[]) => void): string[];
}
/**
* Get value from context (synchronous)
* @param key - Context key or array of keys
* @returns Value or array of values
*/
context.get(key: string | string[]): any;
/**
* Get value from context (asynchronous)
* @param key - Context key or array of keys
* @param callback - Callback function
*/
context.get(key: string | string[], callback: (err: Error, value: any) => void): void;
/**
* Set value in context
* @param key - Context key or array of keys
* @param value - Value to store or array of values
* @param callback - Optional callback function
*/
context.set(key: string | string[], value: any, callback?: (err: Error) => void): void;
/**
* Get all context keys
* @param callback - Optional callback function
* @returns Array of keys (if synchronous)
*/
context.keys(callback?: (err: Error, keys: string[]) => void): string[];Context Storage Examples:
// Node-scoped storage (persists across messages for this node)
const counter = context.get("counter") || 0;
context.set("counter", counter + 1);
// Flow-scoped storage (shared across all nodes in this flow)
const flowData = flow.get("sharedData") || {};
flowData.lastUpdate = Date.now();
flow.set("sharedData", flowData);
// Global storage (shared across all flows)
const globalConfig = global.get("config") || {};
// Store selection with specific store
const value = context.get("key", "file"); // Get from file store
context.set("key", "value", "memory"); // Set in memory store
// Asynchronous operations
context.get("asyncKey", (err, value) => {
if (!err) {
node.log(`Retrieved: ${value}`);
}
});
// Multiple keys
const values = context.get(["key1", "key2", "key3"]);
context.set(["key1", "key2"], ["value1", "value2"]);
// Get all keys
const allKeys = context.keys();Access to environment variables and Node-RED predefined variables.
/**
* Environment variables interface
*/
interface EnvironmentVariables {
get(name: string): string | undefined;
}
/**
* Get environment variable
* @param name - Environment variable name
* @returns Environment variable value or undefined
*/
env.get(name: string): string | undefined;Predefined Environment Variables:
NR_NODE_ID - Current node IDNR_NODE_NAME - Current node nameNR_NODE_PATH - Current node pathNR_GROUP_ID - Parent group ID (if in group)NR_GROUP_NAME - Parent group name (if in group)NR_FLOW_ID - Current flow IDNR_FLOW_NAME - Current flow nameNR_SUBFLOW_ID - Subflow instance ID (if in subflow)NR_SUBFLOW_NAME - Subflow name (if in subflow)NR_SUBFLOW_PATH - Subflow path (if in subflow)Usage Examples:
// System environment variables
const dbHost = env.get("DATABASE_HOST") || "localhost";
const apiKey = env.get("API_KEY");
// Node-RED predefined variables
const nodeId = env.get("NR_NODE_ID");
const nodeName = env.get("NR_NODE_NAME") || "Unnamed";
const flowName = env.get("NR_FLOW_NAME");
// Use in logging
node.log(`Node ${nodeName} (${nodeId}) in flow ${flowName} processing message`);
// Conditional logic based on environment
if (env.get("NODE_ENV") === "development") {
node.log("Debug info:", msg);
}Subset of Node-RED utilities available in Function nodes.
/**
* Node-RED utilities available in Function nodes
*/
interface NodeREDUtilities {
util: {
generateId(): string;
cloneMessage(msg: NodeMessage): NodeMessage;
setMessageProperty(msg: NodeMessage, prop: string, value: any): boolean;
getMessageProperty(msg: NodeMessage, prop: string): any;
};
}
/**
* Generate unique identifier
* @returns Generated ID string
*/
RED.util.generateId(): string;
/**
* Clone message object safely
* @param msg - Message to clone
* @returns Cloned message
*/
RED.util.cloneMessage(msg: NodeMessage): NodeMessage;
/**
* Set property on message using dot notation
* @param msg - Target message object
* @param prop - Property path (e.g., "payload.data.value")
* @param value - Value to set
* @returns true if successful
*/
RED.util.setMessageProperty(msg: NodeMessage, prop: string, value: any): boolean;
/**
* Get property from message using dot notation
* @param msg - Source message object
* @param prop - Property path (e.g., "payload.data.value")
* @returns Property value
*/
RED.util.getMessageProperty(msg: NodeMessage, prop: string): any;Usage Examples:
// Generate IDs for tracking
const requestId = RED.util.generateId();
msg.requestId = requestId;
// Safe message cloning
const backup = RED.util.cloneMessage(msg);
// Dynamic property access
const sensorValue = RED.util.getMessageProperty(msg, "payload.sensors.temperature");
RED.util.setMessageProperty(msg, "payload.processed.timestamp", Date.now());
// Complex property paths
const path = "payload.data.items.0.value";
const value = RED.util.getMessageProperty(msg, path);
if (value !== undefined) {
RED.util.setMessageProperty(msg, "payload.firstItemValue", value);
}// Using callbacks with context
context.get("config", (err, config) => {
if (err) {
node.error("Failed to get config", msg);
return;
}
msg.payload = processWithConfig(msg.payload, config);
node.send(msg);
});
// Using setTimeout for delays
setTimeout(() => {
msg.payload = "Delayed message";
node.send(msg);
}, 5000);// Route messages based on conditions
const route1 = [];
const route2 = [];
msg.payload.forEach(item => {
if (item.type === "urgent") {
route1.push({ payload: item });
} else {
route2.push({ payload: item });
}
});
// Send arrays of messages to different outputs
node.send([route1, route2]);try {
// Risky operation
const result = JSON.parse(msg.payload);
msg.payload = result;
node.send(msg);
} catch (err) {
// Send error to catch node
node.error(err.message, msg);
// Or handle gracefully
node.warn("Invalid JSON, using default");
msg.payload = {};
node.send(msg);
}interface NodeStatus {
fill?: 'red' | 'green' | 'yellow' | 'blue' | 'grey';
shape?: 'ring' | 'dot';
text?: string;
}
interface NodeMessage {
_msgid: string;
topic?: string;
payload: any;
[key: string]: any;
}