Complete Guide to n8n Expressions

Complete Guide to n8n Expressions

Complete Guide to n8n Expressions

Table of Contents

  1. Introduction
  2. Expression Basics
  3. Data Types and Structure
  4. Core Objects and Variables
  5. String Operations
  6. Number Operations
  7. Date and Time
  8. Array Operations
  9. Object Operations
  10. Conditional Logic
  11. Regular Expressions
  12. Advanced Techniques
  13. Common Use Cases
  14. Best Practices
  15. Troubleshooting

Introduction

n8n expressions are JavaScript-based formulas that allow you to dynamically manipulate and transform data in your workflows. They use a templating syntax with double curly braces {{ }} and provide access to powerful built-in functions and variables.

When to Use Expressions

  • Transform data between nodes
  • Create dynamic field values
  • Implement conditional logic
  • Format dates and strings
  • Perform calculations
  • Filter and manipulate arrays

Expression Basics

Syntax

All expressions are wrapped in double curly braces:

{{ expression_here }}

Simple Examples

// Static value
{{ "Hello World" }}

// Accessing input data
{{ $json.name }}

// Simple calculation
{{ 10 + 5 }}

// String concatenation
{{ "Hello " + $json.firstName }}

Multi-line Expressions

For complex expressions, you can use multiple lines:

{{
  const userName = $json.firstName + " " + $json.lastName;
  return userName.toUpperCase();
}}

Data Types and Structure

Understanding n8n Data Structure

n8n passes data between nodes as an array of items, where each item contains:

  • json: The main data object
  • binary: Binary data (files, images, etc.)
  • pairedItem: Reference to source item

Data Type Examples

// String
{{ "This is a string" }}

// Number
{{ 42 }}
{{ 3.14159 }}

// Boolean
{{ true }}
{{ false }}

// Array
{{ ["apple", "banana", "cherry"] }}

// Object
{{ { name: "John", age: 30 } }}

// Null/Undefined
{{ null }}
{{ undefined }}

Core Objects and Variables

$json - Current Item Data

// Access properties
{{ $json.email }}
{{ $json.user.name }}
{{ $json["field-with-spaces"] }}

// Nested access
{{ $json.address.street }}
{{ $json.items[0].price }}

$input - Input Data Access

// Get data from specific input
{{ $input.first().json.name }}
{{ $input.last().json.timestamp }}
{{ $input.item.json.value }}

// Access all items
{{ $input.all() }}

// Get specific item by index
{{ $input.item(0).json.name }}

$node - Access Data from Specific Nodes

// Get data from a named node
{{ $node["HTTP Request"].json.response }}
{{ $node["Set"].json.processedData }}

// Access specific item from node
{{ $node["Webhook"].first().json.body }}

$vars - Workflow Variables

// Access workflow variables
{{ $vars.apiKey }}
{{ $vars.environment }}

$env - Environment Variables

// Access environment variables
{{ $env.API_URL }}
{{ $env.DATABASE_PASSWORD }}

$now - Current Timestamp

// Current Unix timestamp
{{ $now }}

// Current ISO date
{{ newDate($now).toISOString() }}

$today - Today's Date

// Today as ISO string
{{ $today }}

// Format today's date
{{ new Date($today).toLocaleDateString() }}

String Operations

Basic String Methods

// Length
{{ $json.message.length }}

// Case conversion
{{ $json.name.toLowerCase() }}
{{ $json.name.toUpperCase() }}
{{ $json.name.charAt(0).toUpperCase() + $json.name.slice(1).toLowerCase() }}

// Trimming
{{ $json.text.trim() }}
{{ $json.text.trimStart() }}
{{ $json.text.trimEnd() }}

String Manipulation

// Substring
{{ $json.text.substring(0, 10) }}
{{ $json.text.slice(2, -1) }}

// Replace
{{ $json.text.replace("old", "new") }}
{{ $json.text.replaceAll("old", "new") }}

// Split and join
{{ $json.text.split(",") }}
{{ $json.array.join(", ") }}

// Character at position
{{ $json.text.charAt(0) }}
{{ $json.text[0] }}

String Searching

// Check if string contains
{{ $json.text.includes("search term") }}
{{ $json.text.indexOf("search") !== -1 }}

// Starts/ends with
{{ $json.text.startsWith("Hello") }}
{{ $json.text.endsWith("World") }}

// Find position
{{ $json.text.indexOf("search") }}
{{ $json.text.lastIndexOf("search") }}

Template Literals

// Using template literals
{{ Hello ${$json.firstName} ${$json.lastName}! }}
{{ Order #${$json.orderId} total: $${$json.amount} }}

// Multi-line templates
{{
  `Dear ${$json.customerName},

  Your order ${$json.orderNumber} has been processed.
  Total: $${$json.total}

  Thank you!`
}}

Number Operations

Basic Math

// Arithmetic operations
{{ $json.price * 1.08 }}  // Add 8% tax
{{ $json.total - $json.discount }}
{{ $json.quantity / 2 }}
{{ $json.base  2 }}  // Power

// Rounding
{{ Math.round($json.price) }}
{{ Math.floor($json.value) }}
{{ Math.ceil($json.value) }}
{{ Number($json.price.toFixed(2)) }}

Number Utilities

// Convert string to number
{{ Number($json.stringValue) }}
{{ parseInt($json.stringValue) }}
{{ parseFloat($json.stringValue) }}

// Check if number
{{ !isNaN($json.value) }}
{{ Number.isInteger($json.value) }}

// Min/Max
{{ Math.min($json.a, $json.b, $json.c) }}
{{ Math.max($json.values[0], $json.values[1]) }}

// Random numbers
{{ Math.random() }}
{{ Math.floor(Math.random() * 100) }}  // 0-99

Advanced Math

// Math functions
{{ Math.abs($json.value) }}
{{ Math.sqrt($json.number) }}
{{ Math.pow($json.base, $json.exponent) }}

// Trigonometry
{{ Math.sin($json.angle) }}
{{ Math.cos($json.angle) }}
{{ Math.PI }}

Date and Time

Creating Dates

// Current date
{{ newDate() }}
{{ newDate().toISOString() }}

// From timestamp
{{ newDate($json.timestamp) }}
{{ newDate($json.timestamp * 1000) }}  // Unix timestamp// From string
{{ newDate($json.dateString) }}
{{ newDate("2024-01-15T10:30:00Z") }}

Date Formatting

// ISO format
{{ newDate($json.date).toISOString() }}

// Locale formatting
{{ newDate($json.date).toLocaleDateString() }}
{{ newDate($json.date).toLocaleTimeString() }}
{{ newDate($json.date).toLocaleString() }}

// Custom formatting
{{ newDate($json.date).toLocaleDateString('en-US', {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
}) }}

Date Manipulation

// Get components
{{ newDate($json.date).getFullYear() }}
{{ newDate($json.date).getMonth() + 1 }}  // Month is 0-indexed
{{ newDate($json.date).getDate() }}
{{ newDate($json.date).getHours() }}

// Set components
{{
  const date = newDate($json.date);
  date.setDate(date.getDate() + 7);  // Add 7 daysreturndate.toISOString();
}}

// Date arithmetic
{{
  const now = newDate();
  const future = newDate(now.getTime() + (24  60  60 * 1000));  // Add 1 dayreturn future;
}}

Date Comparisons

// Compare dates
{{ newDate($json.startDate) < newDate($json.endDate) }}
{{ newDate($json.date) > newDate() }}  // Is in future// Days between dates
{{
  const start = newDate($json.startDate);
  const end = newDate($json.endDate);
  const diffTime = Math.abs(end - start);
  const diffDays = Math.ceil(diffTime / (1000  60  60 * 24));
  return diffDays;
}}

Array Operations

Basic Array Methods

// Array length
{{ $json.items.length }}

// Access elements
{{ $json.items[0] }}
{{ $json.items[$json.items.length - 1] }}  // Last element// Check if array
{{ Array.isArray($json.items) }}

// Convert to array
{{ Array.from($json.items) }}

Array Manipulation

// Add elements
{{ $json.items.concat(["new item"]) }}
{{ [...$json.items, "new item"] }}  // Spread operator// Remove/modify elements
{{ $json.items.slice(1) }}  // Remove first
{{ $json.items.slice(0, -1) }}  // Remove last
{{ $json.items.slice(1, 3) }}  // Get elements 1-2

Array Searching

// Find elements
{{ $json.items.find(item => item.id === $json.targetId) }}
{{ $json.items.findIndex(item => item.name === "target") }}

// Check existence
{{ $json.items.includes("search term") }}
{{ $json.items.some(item => item.active === true) }}
{{ $json.items.every(item => item.status === "complete") }}

Array Transformation

// Map - transform each element
{{ $json.items.map(item => item.name) }}
{{ $json.items.map(item => ({ ...item, processed: true })) }}

// Filter - select elements
{{ $json.items.filter(item => item.price > 100) }}
{{ $json.items.filter(item => item.category === $json.targetCategory) }}

// Reduce - aggregate
{{ $json.items.reduce((sum, item) => sum + item.price, 0) }}
{{ $json.items.reduce((acc, item) => {
  acc[item.category] = (acc[item.category] || 0) + 1;
  return acc;
}, {}) }}

Array Sorting

// Sort numbers
{{ $json.numbers.sort((a, b) => a - b) }}  // Ascending
{{ $json.numbers.sort((a, b) => b - a) }}  // Descending// Sort objects
{{ $json.items.sort((a, b) => a.name.localeCompare(b.name)) }}
{{ $json.items.sort((a, b) => new Date(a.date) - new Date(b.date)) }}

// Sort by multiple criteria
{{ $json.items.sort((a, b) => {
  if (a.priority !== b.priority) return b.priority - a.priority;
  return a.name.localeCompare(b.name);
}) }}

Object Operations

Object Access

// Property access
{{ $json.user.name }}
{{ $json["property-with-dashes"] }}
{{ $json[propertyName] }}  // Dynamic property access

// Safe access (prevents errors)
{{ $json.user?.address?.street }}
{{ $json.optional?.property || "default value" }}

Object Manipulation

// Get keys/values
{{ Object.keys($json.user) }}
{{ Object.values($json.user) }}
{{ Object.entries($json.user) }}

// Merge objects
{{ Object.assign({}, $json.user, { updated: true }) }}
{{ { ...$json.user, modified: new Date() } }}  // Spread syntax// Create new object
{{ {
  id: $json.id,
  name: $json.firstName + " " + $json.lastName,
  email: $json.email.toLowerCase(),
  timestamp: new Date().toISOString()
} }}

Object Validation

// Check if property exists
{{ $json.hasOwnProperty('email') }}
{{ 'email' in $json }}

// Check if object is empty
{{ Object.keys($json.data).length === 0 }}

// Get nested property safely
{{
  function getNestedProperty(obj, path) {
    return path.split('.').reduce((current, key) => current?.[key], obj);
  }
  return getNestedProperty($json, 'user.profile.settings.theme');
}}

Conditional Logic

If-Else Statements

// Ternary operator
{{ $json.status === "active" ? "✅ Active" : "❌ Inactive" }}
{{ $json.price > 100 ? "expensive" : "affordable" }}

// Multiple conditions
{{ $json.score >= 90 ? "A" : $json.score >= 80 ? "B" : "C" }}

// Complex conditions
{{ ($json.age >= 18 && $json.hasLicense) ? "Can drive" : "Cannot drive" }}

Complex Conditionals

// Using if statements in functions
{{
  if ($json.type === "premium") {
    return $json.price * 0.9;  // 10% discount
  } elseif ($json.type === "regular") {
    return $json.price * 0.95;  // 5% discount
  } else {
    return $json.price;
  }
}}

// Switch-like behavior
{{
  const statusMessages = {
    pending: "⏳ Waiting for approval",
    approved: "✅ Approved",
    rejected: "❌ Rejected",
    default: "❓ Unknown status"
  };
  return statusMessages[$json.status] || statusMessages.default;
}}

Null/Undefined Handling

// Default values
{{ $json.name || "Anonymous" }}
{{ $json.email ?? "no-email@example.com" }}  // Nullish coalescing

// Check for existence
{{ $json.optionalField ? $json.optionalField.toUpperCase() : "N/A" }}

// Safe method calls
{{ $json.text?.trim().toLowerCase() }}

Regular Expressions

Basic Pattern Matching

// Test if pattern matches
{{ /^[A-Z]{2,3}-\d{4}$/.test($json.code) }}  // Format: AB-1234
{{ $json.email.match(/^[^@]+@[^@]+\.[^@]+$/) !== null }}  // Basic email validation// Extract matches
{{ $json.text.match(/\d+/g) }}  // All numbers
{{ $json.phone.match(/\((\d{3})\) (\d{3})-(\d{4})/) }}  // Phone number groups

String Replacement with Regex

// Replace patterns
{{ $json.text.replace(/\s+/g, " ") }}  // Replace multiple spaces with single space
{{ $json.phone.replace(/[^\d]/g, "") }}  // Remove all non-digits// Replace with captured groups
{{ $json.name.replace(/(\w+),\s*(\w+)/, "$2 $1") }}  // "Last, First" → "First Last"

Common Regex Patterns

// Email validation
{{ /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test($json.email) }}

// Phone number extraction
{{ $json.text.match(/(?:\+1[-.\s]?)?\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{4})/) }}

// URL detection
{{ /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/.test($json.text) }}// Extract hashtags
{{ $json.text.match(/#\w+/g) }}

Advanced Techniques

Custom Functions

{{
  // Define reusable functions
  function formatCurrency(amount, currency = 'USD') {
    return new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: currency
    }).format(amount);
  }

  function slugify(text) {
    return text
      .toLowerCase()
      .replace(/[^\w ]+/g, '')
      .replace(/ +/g, '-');
  }

  return {
    formattedPrice: formatCurrency($json.price),
    slug: slugify($json.title)
  };
}}

Error Handling

{{
  try {
    const result = JSON.parse($json.jsonString);
    return result.value;
  } catch (error) {
    return"Invalid JSON";
  }
}}

// Safe property access
{{
  functionsafeGet(obj, path, defaultValue = null) {
    try {
      return path.split('.').reduce((current, key) => current[key], obj);
    } catch {
      return defaultValue;
    }
  }

  return safeGet($json, 'user.profile.settings.theme', 'default');
}}

Working with Multiple Items

// Process all input items
{{
  const allItems = $input.all();
  const processed = allItems.map(item => ({
    id:item.json.id,
    processedAt: new Date().toISOString(),
    data:item.json
  }));
  return processed;
}}

// Aggregate data from multiple items
{{
  const items = $input.all();
  return {
    totalRevenue:items.reduce((sum, item) => sum + item.json.amount, 0),
    averageOrder:items.reduce((sum, item) => sum + item.json.amount, 0) / items.length,
    orderCount:items.length
  };
}}

Dynamic Property Access

// Access properties dynamically
{{
  const fieldName = $json.dynamicField;
  return $json[fieldName];
}}

// Build object with dynamic keys
{{
  const key = $json.categoryName;
  return {
    [key]: $json.value,
    timestamp: new Date(),
    processed: true
  };
}}

Common Use Cases

Data Transformation

// Transform API response
{{
  {
    id: $json.user_id,
    name: ${$json.first_name} ${$json.last_name},
    email: $json.email_address.toLowerCase(),
    isActive: $json.status === 'active',
    lastLogin: new Date($json.last_login_timestamp * 1000).toISOString(),
    profile: {
      avatar: $json.avatar_url,
      bio: $json.bio || 'No bio available'
    }
  }
}}

URL Building

// Build API endpoint
{{ https://api.example.com/v1/users/${$json.userId}/orders?status=${$json.status}&limit=10 }}

// Build webhook URL with query parameters
{{
  const baseUrl = 'https://webhook.site/';
  const params = new URLSearchParams({
    source: 'n8n',
    timestamp: Date.now(),
    userId: $json.userId
  });
  return${baseUrl}?${params.toString()};
}}

Form Data Processing

// Process form submission
{{
  const requiredFields = ['name', 'email', 'message'];
  const missingFields = requiredFields.filter(field => !$json[field] || $json[field].trim() === '');

  if (missingFields.length > 0) {
    return {
      success: false,
      error: Missing required fields: ${missingFields.join(', ')}
    };
  }

  return {
    success: true,
    data: {
      name: $json.name.trim(),
      email: $json.email.toLowerCase().trim(),
      message: $json.message.trim(),
      submittedAt: new Date().toISOString()
    }
  };
}}

Text Processing

// Clean and process text
{{
  const text = $json.content || '';
  return {
    original: text,
    cleaned: text.trim().replace(/\s+/g, ' '),
    wordCount: text.trim().split(/\s+/).length,
    hasHashtags: text.includes('#'),
    hashtags: text.match(/#\w+/g) || [],
    mentions: text.match(/@\w+/g) || [],
    links: text.match(/https?:\/\/[^\s]+/g) || []
  };
}}

CSV/Data Export Formatting

// Prepare data for CSV export
{{
  const items = $input.all();
  return items.map(item => ({
    'Order ID': item.json.id,
    'Customer Name': ${item.json.firstName}${item.json.lastName},
    'Email': item.json.email,
    'Total Amount': $${item.json.total.toFixed(2)},
    'Order Date': newDate(item.json.createdAt).toLocaleDateString(),
    'Status': item.json.status.toUpperCase()
  }));
}}

Best Practices

Performance Optimization

  • Use $input.first().json instead of $json when you need the first item specifically
  • Cache expensive operations in variables
  • Avoid nested loops when possible
  • Use built-in methods like map, filter, reduce instead of manual loops

Code Organization

// Good: Organize complex logic
{{
  // Configurationconst config = {
    taxRate: 0.08,
    discountThreshold: 100,
    discountRate: 0.1
  };

  // Helper functionsconst calculateDiscount = (amount) => {
    return amount >= config.discountThreshold ? amount * config.discountRate : 0;
  };

  const calculateTax = (amount) => {
    return amount * config.taxRate;
  };

  // Main logicconst subtotal = $json.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
  const discount = calculateDiscount(subtotal);
  const taxableAmount = subtotal - discount;
  const tax = calculateTax(taxableAmount);
  const total = taxableAmount + tax;

  return {
    subtotal,
    discount,
    tax,
    total: Math.round(total * 100) / 100// Round to 2 decimal places
  };
}}

Error Prevention

// Always validate data before processing
{{
  if (!$json || typeof $json !== 'object') {
    return { error: 'Invalid input data' };
  }

  if (!$json.email || typeof $json.email !== 'string') {
    return { error: 'Email is required' };
  }

  // Safe processingreturn {
    email: $json.email.toLowerCase().trim(),
    domain: $json.email.split('@')[1] || 'unknown'
  };
}}

Debugging Techniques

// Add debugging information
{{
  const debugInfo = {
    inputType: typeof $json,
    inputKeys: Object.keys($json || {}),
    nodeData: $node,
    timestamp: newDate().toISOString()
  };

  console.log('Debug info:', debugInfo);  // Visible in browser consolereturn {
    result: $json.processedValue,
    debug: debugInfo
  };
}}

Troubleshooting

Common Errors and Solutions

"Cannot read property of undefined"

// Problem: Accessing undefined properties
{{ $json.user.name }}  // Error if user is undefined

// Solution: Use optional chaining
{{ $json.user?.name }}
{{ $json.user?.name || "Unknown" }}

"Expression failed to resolve"

// Problem: Syntax errors or undefined variables
{{ $json.nonExistentField.toUpperCase() }}

// Solution: Add existence checks
{{ $json.nonExistentField ? $json.nonExistentField.toUpperCase() : "" }}

"Unexpected token" in JSON

// Problem: Malformed JSON strings
{{ JSON.parse($json.jsonString) }}

// Solution: Add error handling
{{
  try {
    return JSON.parse($json.jsonString);
  } catch (error) {
    return { error: "Invalid JSON", original: $json.jsonString };
  }
}}

Debugging Tips

  1. Use console.log(): Add console.log() statements to see intermediate values
  2. Break down complex expressions: Split complex logic into smaller, testable parts
  3. Check data structure: Use Object.keys($json) to see available properties
  4. Validate assumptions: Always check if data exists before using it
  5. Test with sample data: Create test nodes with known data structures

Testing Expressions

Create a "Set" node to test expressions:

{{
  // Test dataconst testData = {
    name: "John Doe",
    email: "john@example.com",
    orders: [
      { id: 1, total: 100 },
      { id: 2, total: 250 }
    ]
  };

  // Test your expression logic hereconst result = testData.orders.reduce((sum, order) => sum + order.total, 0);

  return {
    testData,
    result,
    success: true
  };
}}

Conclusion

n8n expressions are a powerful tool for data manipulation and workflow automation. Master these concepts and techniques to build more sophisticated and reliable workflows. Remember to:

  • Start simple and build complexity gradually
  • Always handle edge cases and errors
  • Use meaningful variable names and comments
  • Test thoroughly with various data inputs
  • Keep expressions readable and maintainable

With practice, you'll be able to handle complex data transformations and create dynamic, responsive workflows that adapt to your specific needs.

Ref: https://github.com/panaversity/learn-low-code-agentic-ai/blob/main/03_code_expressions/complete_guide.md