function
Execute custom JavaScript code to transform and manipulate data
function Block
The function block allows you to execute custom JavaScript code to transform and manipulate data flowing through your workflows. It's one of the most powerful and flexible blocks for implementing custom logic and data transformations.
Overview
The function block provides a JavaScript execution environment where you can write custom code to process messages, transform data, make calculations, and implement complex business logic that isn't available in other blocks.
Configuration Options
Code Editor
- JavaScript Code: Write your custom JavaScript function
- Setup Tab: Initialize variables and import libraries (runs once)
- On Start Tab: Code that runs when the flow starts
- On Stop Tab: Code that runs when the flow stops
Function Environment
- Available Objects:
msg: The incoming message objectnode: Reference to the current nodecontext: Node-specific persistent storageflow: Flow-wide persistent storageglobal: Global persistent storageenv: Environment variables
Return Options
- Return Message: Use
return msgto pass the modified message - Send Message: Use
node.send(msg)to send message(s) - Multiple Outputs: Send different messages to multiple outputs
- No Output: Perform side effects without sending messages
Basic Function Structure
Simple Transformation
// Transform the payload
msg.payload = msg.payload.toUpperCase();
return msg;Multiple Operations
// Multiple transformations
msg.payload.processed = true;
msg.payload.timestamp = new Date().toISOString();
msg.payload.count = (msg.payload.count || 0) + 1;
return msg;Conditional Logic
// Conditional processing
if (msg.payload.type === "urgent") {
msg.priority = "high";
msg.payload.escalated = true;
} else {
msg.priority = "normal";
}
return msg;Common Use Cases
Data Transformation
Convert data formats and structures:
// Transform array to object
const items = msg.payload.items;
const itemsById = {};
items.forEach((item) => {
itemsById[item.id] = item;
});
msg.payload = itemsById;
return msg;Calculations and Processing
Perform mathematical operations:
// Calculate totals
const items = msg.payload.line_items;
let total = 0;
let tax = 0;
items.forEach((item) => {
const itemTotal = item.quantity * item.price;
total += itemTotal;
tax += itemTotal * 0.1; // 10% tax
});
msg.payload.subtotal = total;
msg.payload.tax = tax;
msg.payload.total = total + tax;
return msg;String Manipulation
Process and clean text data:
// Clean and format text
let text = msg.payload.text;
// Remove extra whitespace
text = text.replace(/\s+/g, " ").trim();
// Extract email addresses
const emails = text.match(/[\w.-]+@[\w.-]+\.\w+/g) || [];
// Extract phone numbers
const phones = text.match(/\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}/g) || [];
msg.payload = {
cleaned_text: text,
extracted_emails: emails,
extracted_phones: phones,
};
return msg;Data Validation
Validate incoming data:
// Validate required fields
const required = ["name", "email", "phone"];
const missing = [];
required.forEach((field) => {
if (!msg.payload[field]) {
missing.push(field);
}
});
if (missing.length > 0) {
msg.payload = {
error: "Missing required fields",
missing_fields: missing,
valid: false,
};
} else {
msg.payload.valid = true;
}
return msg;Array Processing
Work with arrays and collections:
// Filter and sort array
const documents = msg.payload.documents;
// Filter by date range
const startDate = new Date("2024-01-01");
const endDate = new Date("2024-12-31");
const filtered = documents.filter((doc) => {
const docDate = new Date(doc.date);
return docDate >= startDate && docDate <= endDate;
});
// Sort by confidence score
const sorted = filtered.sort((a, b) => b.confidence - a.confidence);
msg.payload = {
total_documents: documents.length,
filtered_count: sorted.length,
documents: sorted,
};
return msg;Advanced Features
Multiple Outputs
Send different messages to different outputs:
// Create different outputs based on conditions
const inputData = msg.payload;
if (inputData.type === "invoice") {
const invoiceMsg = { ...msg, payload: { type: "invoice", data: inputData } };
node.send([invoiceMsg, null, null]); // Send to first output
} else if (inputData.type === "receipt") {
const receiptMsg = { ...msg, payload: { type: "receipt", data: inputData } };
node.send([null, receiptMsg, null]); // Send to second output
} else {
const unknownMsg = { ...msg, payload: { type: "unknown", data: inputData } };
node.send([null, null, unknownMsg]); // Send to third output
}Context Storage
Store data between messages:
// Get previous count from context
let count = context.get("message_count") || 0;
count++;
// Store updated count
context.set("message_count", count);
// Add count to message
msg.payload.message_number = count;
msg.payload.timestamp = new Date().toISOString();
return msg;Error Handling
Handle errors gracefully:
try {
// Risky operation
const data = JSON.parse(msg.payload.json_string);
msg.payload = {
success: true,
parsed_data: data,
};
} catch (error) {
msg.payload = {
success: false,
error: error.message,
original_data: msg.payload.json_string,
};
}
return msg;Async Operations
Handle asynchronous operations:
// For async operations, use node.send() instead of return
async function processData() {
try {
// Simulate async operation
await new Promise((resolve) => setTimeout(resolve, 1000));
msg.payload.processed = true;
msg.payload.processing_time = "1000ms";
node.send(msg);
} catch (error) {
msg.payload = {
error: error.message,
success: false,
};
node.send(msg);
}
}
processData();Best Practices
- Keep it simple: Use built-in blocks when possible, function blocks for custom logic
- Error handling: Always include try-catch for risky operations
- Performance: Avoid heavy computations that could block the flow
- Documentation: Add comments to explain complex logic
- Testing: Use debug blocks to test function outputs
Common Patterns
Data Enrichment
// Add metadata to existing data
msg.payload.metadata = {
processed_at: new Date().toISOString(),
node_id: node.id,
flow_name: "Document Processing",
version: "1.0",
};Format Conversion
// Convert between formats
const csvData = msg.payload.csv;
const jsonData = csvData.map((row) => ({
id: row[0],
name: row[1],
email: row[2],
date: new Date(row[3]).toISOString(),
}));
msg.payload = jsonData;Business Logic
// Implement business rules
const order = msg.payload;
let discount = 0;
if (order.customer_type === "premium") {
discount = 0.15; // 15% discount
} else if (order.total > 1000) {
discount = 0.1; // 10% discount for large orders
} else if (order.items.length > 5) {
discount = 0.05; // 5% discount for bulk orders
}
order.discount_rate = discount;
order.discount_amount = order.total * discount;
order.final_total = order.total - order.discount_amount;Debugging Tips
- Use
console.log()to debug (output appears in Node-RED logs) - Use
node.warn()andnode.error()for different log levels - Check the debug panel for message structure
- Test with simple inputs first, then increase complexity
- Use try-catch blocks to handle edge cases
Performance Considerations
- Avoid blocking operations (use async patterns for heavy work)
- Don't store large objects in context (use external storage)
- Process arrays efficiently (consider chunking for very large arrays)
- Use appropriate data structures for your operations
The function block is most powerful when combined with other processing blocks. Use debug blocks to test your functions and switch blocks for routing based on function results.