n8n-best-practices
n8n Best Practices
When to use this skill: Any time you're working with n8n workflows and encounter errors, unexpected behavior, or need guidance on implementation patterns.
Critical Knowledge Base
🚨 ES5 Syntax Only in Code Nodes
n8n Code nodes only support ES5 JavaScript. Modern ES6+ syntax will fail.
❌ DO NOT USE:
// Arrow functions
const process = (item) => item.value;
// Template literals
const url = `https://api.example.com/${id}`;
// const/let
const value = 123;
// Destructuring
const { name, age } = person;
// Spread operator in function calls
Math.max(...numbers);
✅ USE INSTEAD:
// Regular functions
var process = function(item) {
return item.value;
};
// String concatenation
var url = 'https://api.example.com/' + id;
// var only
var value = 123;
// Manual assignment
var name = person.name;
var age = person.age;
// apply() for spread
Math.max.apply(null, numbers);
// Object spread alternative
var merged = Object.assign({}, obj1, obj2);
🌐 HTTP Requests in Code Nodes
CRITICAL: $http and axios are NOT available in Code nodes.
❌ WRONG:
const response = await $http.request({url: 'https://api.example.com'});
const axios = require('axios');
✅ CORRECT - Use Native HTTPS:
var https = require('https');
function makeRequest(url, options) {
return new Promise(function(resolve, reject) {
var req = https.request(options, function(res) {
var data = '';
res.on('data', function(chunk) { data += chunk; });
res.on('end', function() {
resolve({statusCode: res.statusCode, body: data});
});
});
req.on('error', reject);
req.end();
});
}
// Usage
var result = await makeRequest('example.com', {
hostname: 'example.com',
path: '/api/endpoint',
method: 'POST',
headers: {'Content-Type': 'application/json'}
});
🔐 Environment Variables Configuration
CRITICAL: Variables must be explicitly exported, not just sourced.
❌ WRONG:
source .env.local
pnpm start
# Variables will be undefined in Code nodes!
✅ CORRECT:
#!/bin/bash
export NOTION_API_KEY=$(grep NOTION_API_KEY .env.local | cut -d '=' -f2)
export QWEN_API_KEY=$(grep QWEN_API_KEY .env.local | cut -d '=' -f2)
export NODE_FUNCTION_ALLOW_BUILTIN=*
export N8N_BLOCK_ENV_ACCESS_IN_NODE=false
pnpm start
Access in Code nodes:
var apiKey = process.env.NOTION_API_KEY; // Not $env
📦 gzip Compression Issues
Problem: HTTP requests with gzip compression return garbled data.
❌ WRONG:
headers: {
'Accept-Encoding': 'gzip, deflate'
}
// Returns: �������
✅ CORRECT:
headers: {
'Content-Type': 'application/json'
// DO NOT include Accept-Encoding header
}
🔄 Data Flow Patterns
Always preserve upstream data:
✅ CORRECT:
return {
json: Object.assign({}, $input.item.json, {
newField: newValue
})
};
❌ WRONG:
return {
json: {
newField: newValue // Loses all previous data!
}
};
📋 JSON Parsing Best Practices
❌ WRONG:
var data = JSON.parse(response);
// May return undefined without error
✅ CORRECT:
function safeJSONParse(text) {
try {
var parsed = JSON.parse(text);
if (parsed && typeof parsed === 'object') {
return parsed;
}
throw new Error('Parsed value is not an object');
} catch (e) {
throw new Error('JSON parsing failed: ' + e.message);
}
}
var data = safeJSONParse(response);
🔧 Code Node Sandbox Configuration
macOS launchd service:
<key>EnvironmentVariables</key>
<dict>
<key>NODE_FUNCTION_ALLOW_BUILTIN</key>
<string>*</string>
</dict>
Shell script:
export NODE_FUNCTION_ALLOW_BUILTIN=*
n8n start
🐛 Common Pitfalls
1. Aggregate Node Array Wrapping
// Aggregate converts: {field: value} → {field: [value]}
var data = $input.first().json;
var actualValue = data.field[0]; // Must access array
2. Split Into Batches Data Loss
// ❌ WRONG: Loses data
return items.map(function(item) {
return {json: {processed: true}};
});
// ✅ CORRECT: Preserves data
return items.map(function(item) {
return {
json: Object.assign({}, item.json, {processed: true})
};
});
3. require() Module Access
// Only available if NODE_FUNCTION_ALLOW_BUILTIN=*
var https = require('https'); // ✅ Works
var axios = require('axios'); // ❌ Not available
Quick Reference
When Code node fails:
- Check ES5 syntax (no arrow functions, template literals)
- Verify environment variables are exported
- Use
require('https')not$http - Remove
Accept-Encodingheader for gzip issues
When data is lost:
- Always spread previous data:
Object.assign({}, $input.item.json, newData) - Check Aggregate node array wrapping
- Verify Split Into Batches preserves data
When environment variables are undefined:
- Use
export, notsource - Access with
process.env.VAR, not$env.VAR - Set
N8N_BLOCK_ENV_ACCESS_IN_NODE=false
Full Documentation
For complete details, examples, and advanced patterns, see:
/mnt/d/work/n8n_agent/n8n-skills/n8n-best-practices/README.md