n8n-best-practices

Use when encountering n8n workflow issues, Code node errors, HTTP requests failing, data flow problems, environment variables not working, JSON parsing errors, or need n8n development patterns and debugging strategies

$ 安裝

git clone https://github.com/majiayu000/claude-skill-registry /tmp/claude-skill-registry && cp -r /tmp/claude-skill-registry/skills/development/n8n-best-practices ~/.claude/skills/claude-skill-registry

// tip: Run this command in your terminal to install the skill


name: n8n-best-practices description: Use when encountering n8n workflow issues, Code node errors, HTTP requests failing, data flow problems, environment variables not working, JSON parsing errors, or need n8n development patterns and debugging strategies

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:

  1. Check ES5 syntax (no arrow functions, template literals)
  2. Verify environment variables are exported
  3. Use require('https') not $http
  4. Remove Accept-Encoding header for gzip issues

When data is lost:

  1. Always spread previous data: Object.assign({}, $input.item.json, newData)
  2. Check Aggregate node array wrapping
  3. Verify Split Into Batches preserves data

When environment variables are undefined:

  1. Use export, not source
  2. Access with process.env.VAR, not $env.VAR
  3. 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