0
# Function Nodes
1
2
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.
3
4
## Capabilities
5
6
### Function Node Context
7
8
Global objects and APIs available in the Function node execution environment.
9
10
```javascript { .api }
11
/**
12
* Available in Function node context
13
*/
14
interface FunctionNodeGlobals {
15
/** Current message being processed */
16
msg: NodeMessage;
17
/** Node instance with control methods */
18
node: NodeInstance;
19
/** Node-scoped persistent storage */
20
context: ContextStore;
21
/** Flow-scoped persistent storage */
22
flow: ContextStore;
23
/** Global-scoped persistent storage */
24
global: ContextStore;
25
/** Environment variables access */
26
env: EnvironmentVariables;
27
/** Node-RED utilities subset */
28
RED: NodeREDUtilities;
29
}
30
31
/**
32
* Message object structure
33
*/
34
interface NodeMessage {
35
/** Unique message identifier */
36
_msgid: string;
37
/** Message topic (optional) */
38
topic?: string;
39
/** Message payload (any type) */
40
payload: any;
41
/** Additional message properties */
42
[key: string]: any;
43
}
44
```
45
46
**Basic Function Node Example:**
47
48
```javascript
49
// Simple message transformation
50
msg.payload = msg.payload.toString().toUpperCase();
51
msg.timestamp = Date.now();
52
return msg;
53
54
// Multiple outputs
55
if (msg.payload > 10) {
56
return [msg, null]; // Send to first output only
57
} else {
58
return [null, msg]; // Send to second output only
59
}
60
61
// Multiple messages to same output
62
return [
63
[
64
{ payload: "First message" },
65
{ payload: "Second message" }
66
],
67
null
68
];
69
```
70
71
### Node Instance Methods
72
73
Methods available on the `node` object for controlling node behavior and output.
74
75
```javascript { .api }
76
/**
77
* Node instance methods
78
*/
79
interface NodeInstance {
80
send(msg: NodeMessage | NodeMessage[], clone?: boolean): void;
81
error(err: string | Error, msg?: NodeMessage): void;
82
warn(warning: string | object): void;
83
log(info: string | object): void;
84
status(status: NodeStatus | string | boolean | number): void;
85
done(err?: Error): void;
86
}
87
88
/**
89
* Send message(s) to connected nodes
90
* @param msg - Message or array of messages to send
91
* @param clone - Whether to clone messages before sending (default: true)
92
*/
93
node.send(msg: NodeMessage | NodeMessage[], clone?: boolean): void;
94
95
/**
96
* Log error and trigger catch nodes
97
* @param err - Error message or Error object
98
* @param msg - Optional message that caused the error
99
*/
100
node.error(err: string | Error, msg?: NodeMessage): void;
101
102
/**
103
* Log warning message
104
* @param warning - Warning message or object
105
*/
106
node.warn(warning: string | object): void;
107
108
/**
109
* Log informational message
110
* @param info - Info message or object
111
*/
112
node.log(info: string | object): void;
113
114
/**
115
* Set node status indicator
116
* @param status - Status object, string, boolean, or number
117
*/
118
node.status(status: NodeStatus | string | boolean | number): void;
119
```
120
121
**Usage Examples:**
122
123
```javascript
124
// Send processed message
125
msg.payload = processData(msg.payload);
126
node.send(msg);
127
128
// Send without cloning (more efficient but potentially unsafe)
129
node.send(msg, false);
130
131
// Error handling
132
try {
133
msg.payload = JSON.parse(msg.payload);
134
node.send(msg);
135
} catch (err) {
136
node.error("Invalid JSON in payload", msg);
137
}
138
139
// Status updates
140
node.status({ fill: "blue", shape: "ring", text: "processing..." });
141
// Clear status
142
node.status({});
143
144
// Logging
145
node.log(`Processing message with topic: ${msg.topic}`);
146
node.warn("Deprecated feature used");
147
```
148
149
### Context Storage
150
151
Persistent storage system available at node, flow, and global scopes.
152
153
```javascript { .api }
154
/**
155
* Context storage interface
156
*/
157
interface ContextStore {
158
get(key: string, callback?: (err: Error, value: any) => void): any;
159
get(keys: string[], callback?: (err: Error, values: any[]) => void): any[];
160
set(key: string, value: any, callback?: (err: Error) => void): void;
161
set(keys: string[], values: any[], callback?: (err: Error) => void): void;
162
keys(callback?: (err: Error, keys: string[]) => void): string[];
163
}
164
165
/**
166
* Get value from context (synchronous)
167
* @param key - Context key or array of keys
168
* @returns Value or array of values
169
*/
170
context.get(key: string | string[]): any;
171
172
/**
173
* Get value from context (asynchronous)
174
* @param key - Context key or array of keys
175
* @param callback - Callback function
176
*/
177
context.get(key: string | string[], callback: (err: Error, value: any) => void): void;
178
179
/**
180
* Set value in context
181
* @param key - Context key or array of keys
182
* @param value - Value to store or array of values
183
* @param callback - Optional callback function
184
*/
185
context.set(key: string | string[], value: any, callback?: (err: Error) => void): void;
186
187
/**
188
* Get all context keys
189
* @param callback - Optional callback function
190
* @returns Array of keys (if synchronous)
191
*/
192
context.keys(callback?: (err: Error, keys: string[]) => void): string[];
193
```
194
195
**Context Storage Examples:**
196
197
```javascript
198
// Node-scoped storage (persists across messages for this node)
199
const counter = context.get("counter") || 0;
200
context.set("counter", counter + 1);
201
202
// Flow-scoped storage (shared across all nodes in this flow)
203
const flowData = flow.get("sharedData") || {};
204
flowData.lastUpdate = Date.now();
205
flow.set("sharedData", flowData);
206
207
// Global storage (shared across all flows)
208
const globalConfig = global.get("config") || {};
209
210
// Store selection with specific store
211
const value = context.get("key", "file"); // Get from file store
212
context.set("key", "value", "memory"); // Set in memory store
213
214
// Asynchronous operations
215
context.get("asyncKey", (err, value) => {
216
if (!err) {
217
node.log(`Retrieved: ${value}`);
218
}
219
});
220
221
// Multiple keys
222
const values = context.get(["key1", "key2", "key3"]);
223
context.set(["key1", "key2"], ["value1", "value2"]);
224
225
// Get all keys
226
const allKeys = context.keys();
227
```
228
229
### Environment Variables
230
231
Access to environment variables and Node-RED predefined variables.
232
233
```javascript { .api }
234
/**
235
* Environment variables interface
236
*/
237
interface EnvironmentVariables {
238
get(name: string): string | undefined;
239
}
240
241
/**
242
* Get environment variable
243
* @param name - Environment variable name
244
* @returns Environment variable value or undefined
245
*/
246
env.get(name: string): string | undefined;
247
```
248
249
**Predefined Environment Variables:**
250
251
- `NR_NODE_ID` - Current node ID
252
- `NR_NODE_NAME` - Current node name
253
- `NR_NODE_PATH` - Current node path
254
- `NR_GROUP_ID` - Parent group ID (if in group)
255
- `NR_GROUP_NAME` - Parent group name (if in group)
256
- `NR_FLOW_ID` - Current flow ID
257
- `NR_FLOW_NAME` - Current flow name
258
- `NR_SUBFLOW_ID` - Subflow instance ID (if in subflow)
259
- `NR_SUBFLOW_NAME` - Subflow name (if in subflow)
260
- `NR_SUBFLOW_PATH` - Subflow path (if in subflow)
261
262
**Usage Examples:**
263
264
```javascript
265
// System environment variables
266
const dbHost = env.get("DATABASE_HOST") || "localhost";
267
const apiKey = env.get("API_KEY");
268
269
// Node-RED predefined variables
270
const nodeId = env.get("NR_NODE_ID");
271
const nodeName = env.get("NR_NODE_NAME") || "Unnamed";
272
const flowName = env.get("NR_FLOW_NAME");
273
274
// Use in logging
275
node.log(`Node ${nodeName} (${nodeId}) in flow ${flowName} processing message`);
276
277
// Conditional logic based on environment
278
if (env.get("NODE_ENV") === "development") {
279
node.log("Debug info:", msg);
280
}
281
```
282
283
### Node-RED Utilities
284
285
Subset of Node-RED utilities available in Function nodes.
286
287
```javascript { .api }
288
/**
289
* Node-RED utilities available in Function nodes
290
*/
291
interface NodeREDUtilities {
292
util: {
293
generateId(): string;
294
cloneMessage(msg: NodeMessage): NodeMessage;
295
setMessageProperty(msg: NodeMessage, prop: string, value: any): boolean;
296
getMessageProperty(msg: NodeMessage, prop: string): any;
297
};
298
}
299
300
/**
301
* Generate unique identifier
302
* @returns Generated ID string
303
*/
304
RED.util.generateId(): string;
305
306
/**
307
* Clone message object safely
308
* @param msg - Message to clone
309
* @returns Cloned message
310
*/
311
RED.util.cloneMessage(msg: NodeMessage): NodeMessage;
312
313
/**
314
* Set property on message using dot notation
315
* @param msg - Target message object
316
* @param prop - Property path (e.g., "payload.data.value")
317
* @param value - Value to set
318
* @returns true if successful
319
*/
320
RED.util.setMessageProperty(msg: NodeMessage, prop: string, value: any): boolean;
321
322
/**
323
* Get property from message using dot notation
324
* @param msg - Source message object
325
* @param prop - Property path (e.g., "payload.data.value")
326
* @returns Property value
327
*/
328
RED.util.getMessageProperty(msg: NodeMessage, prop: string): any;
329
```
330
331
**Usage Examples:**
332
333
```javascript
334
// Generate IDs for tracking
335
const requestId = RED.util.generateId();
336
msg.requestId = requestId;
337
338
// Safe message cloning
339
const backup = RED.util.cloneMessage(msg);
340
341
// Dynamic property access
342
const sensorValue = RED.util.getMessageProperty(msg, "payload.sensors.temperature");
343
RED.util.setMessageProperty(msg, "payload.processed.timestamp", Date.now());
344
345
// Complex property paths
346
const path = "payload.data.items.0.value";
347
const value = RED.util.getMessageProperty(msg, path);
348
if (value !== undefined) {
349
RED.util.setMessageProperty(msg, "payload.firstItemValue", value);
350
}
351
```
352
353
## Advanced Patterns
354
355
### Asynchronous Operations
356
357
```javascript
358
// Using callbacks with context
359
context.get("config", (err, config) => {
360
if (err) {
361
node.error("Failed to get config", msg);
362
return;
363
}
364
365
msg.payload = processWithConfig(msg.payload, config);
366
node.send(msg);
367
});
368
369
// Using setTimeout for delays
370
setTimeout(() => {
371
msg.payload = "Delayed message";
372
node.send(msg);
373
}, 5000);
374
```
375
376
### Multiple Output Handling
377
378
```javascript
379
// Route messages based on conditions
380
const route1 = [];
381
const route2 = [];
382
383
msg.payload.forEach(item => {
384
if (item.type === "urgent") {
385
route1.push({ payload: item });
386
} else {
387
route2.push({ payload: item });
388
}
389
});
390
391
// Send arrays of messages to different outputs
392
node.send([route1, route2]);
393
```
394
395
### Error Handling Patterns
396
397
```javascript
398
try {
399
// Risky operation
400
const result = JSON.parse(msg.payload);
401
msg.payload = result;
402
node.send(msg);
403
} catch (err) {
404
// Send error to catch node
405
node.error(err.message, msg);
406
// Or handle gracefully
407
node.warn("Invalid JSON, using default");
408
msg.payload = {};
409
node.send(msg);
410
}
411
```
412
413
## Types
414
415
```javascript { .api }
416
interface NodeStatus {
417
fill?: 'red' | 'green' | 'yellow' | 'blue' | 'grey';
418
shape?: 'ring' | 'dot';
419
text?: string;
420
}
421
422
interface NodeMessage {
423
_msgid: string;
424
topic?: string;
425
payload: any;
426
[key: string]: any;
427
}
428
```