n8n Workflow Patterns
Build robust workflow automations with n8n - the open-source workflow automation tool.
Overview
n8n is a self-hostable workflow automation platform that connects apps and services. Key features:
-
Visual workflow builder with 400+ integrations
-
Self-hosted or cloud deployment
-
Code nodes for custom logic (JavaScript/Python)
-
Webhook triggers for real-time automation
-
Sub-workflows for modular design
Core Concepts
Workflow Structure
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Trigger │───▶│ Node │───▶│ Output │ │ (Start) │ │ (Process) │ │ (Action) │ └─────────────┘ └─────────────┘ └─────────────┘
Node Types
Type Purpose Examples
Trigger Start workflow Webhook, Schedule, App trigger
Action Perform operations HTTP Request, Database, Email
Transform Modify data Set, Code, IF, Switch
Flow Control execution Merge, Split, Wait, Loop
Trigger Patterns
Webhook Trigger
{ "nodes": [ { "name": "Webhook", "type": "n8n-nodes-base.webhook", "parameters": { "httpMethod": "POST", "path": "my-webhook", "responseMode": "responseNode", "options": { "rawBody": true } } } ] }
Best Practices:
-
Use responseNode for custom responses
-
Enable rawBody for signature verification
-
Add authentication (Header Auth, Basic Auth)
Schedule Trigger
{ "name": "Schedule Trigger", "type": "n8n-nodes-base.scheduleTrigger", "parameters": { "rule": { "interval": [ { "field": "cronExpression", "expression": "0 9 * * 1-5" } ] } } }
Common Schedules:
-
0 * * * *
-
Every hour
-
0 9 * * 1-5
-
Weekdays at 9 AM
-
0 0 * * 0
-
Weekly on Sunday midnight
-
*/15 * * * *
-
Every 15 minutes
App Trigger (Polling)
{ "name": "GitHub Trigger", "type": "n8n-nodes-base.githubTrigger", "parameters": { "owner": "{{$env.GITHUB_OWNER}}", "repository": "{{$env.GITHUB_REPO}}", "events": ["issues", "pull_request"] } }
Data Transformation Patterns
Set Node (Transform Data)
{ "name": "Transform Data", "type": "n8n-nodes-base.set", "parameters": { "mode": "manual", "duplicateItem": false, "assignments": { "assignments": [ { "name": "fullName", "value": "={{ $json.firstName }} {{ $json.lastName }}", "type": "string" }, { "name": "timestamp", "value": "={{ DateTime.now().toISO() }}", "type": "string" } ] } } }
Code Node (JavaScript)
// Process items with custom logic const results = [];
for (const item of $input.all()) { const data = item.json;
// Transform data results.push({ json: { id: data.id, processed: true, score: calculateScore(data), timestamp: new Date().toISOString() } }); }
function calculateScore(data) { return data.value * 0.8 + data.bonus * 0.2; }
return results;
Code Node (Python)
Enable Python in n8n settings
import json from datetime import datetime
results = []
for item in _input.all(): data = item.json
# Transform data
results.append({
"json": {
"id": data.get("id"),
"processed": True,
"timestamp": datetime.now().isoformat()
}
})
return results
Control Flow Patterns
IF Node (Conditional)
{ "name": "Check Status", "type": "n8n-nodes-base.if", "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "leftValue": "={{ $json.status }}", "rightValue": "active", "operator": { "type": "string", "operation": "equals" } } ], "combinator": "and" } } }
Switch Node (Multi-branch)
{ "name": "Route by Type", "type": "n8n-nodes-base.switch", "parameters": { "mode": "rules", "rules": { "values": [ { "outputKey": "order", "conditions": { "conditions": [ { "leftValue": "={{ $json.type }}", "rightValue": "order", "operator": { "type": "string", "operation": "equals" } } ] } }, { "outputKey": "refund", "conditions": { "conditions": [ { "leftValue": "={{ $json.type }}", "rightValue": "refund", "operator": { "type": "string", "operation": "equals" } } ] } } ] }, "fallbackOutput": "extra" } }
Loop Over Items
{ "name": "Loop Over Items", "type": "n8n-nodes-base.splitInBatches", "parameters": { "batchSize": 10, "options": { "reset": false } } }
Merge Node (Combine Data)
{ "name": "Merge Results", "type": "n8n-nodes-base.merge", "parameters": { "mode": "combine", "mergeByFields": { "values": [ { "field1": "id", "field2": "userId" } ] }, "options": {} } }
Error Handling Patterns
Try/Catch with Error Trigger
{ "nodes": [ { "name": "Error Trigger", "type": "n8n-nodes-base.errorTrigger", "parameters": {} }, { "name": "Send Alert", "type": "n8n-nodes-base.slack", "parameters": { "channel": "#alerts", "text": "Workflow failed: {{ $json.workflow.name }}\nError: {{ $json.execution.error.message }}" } } ] }
Retry on Failure
{ "name": "HTTP Request", "type": "n8n-nodes-base.httpRequest", "parameters": { "url": "https://api.example.com/data", "options": {} }, "retryOnFail": true, "maxTries": 3, "waitBetweenTries": 1000 }
Stop and Error Node
{ "name": "Validation Failed", "type": "n8n-nodes-base.stopAndError", "parameters": { "errorMessage": "Invalid input: {{ $json.error }}" } }
Sub-Workflow Pattern
Execute Workflow Node
{ "name": "Process Order", "type": "n8n-nodes-base.executeWorkflow", "parameters": { "source": "database", "workflowId": "order-processing-workflow-id", "mode": "each", "options": { "waitForSubWorkflow": true } } }
Best Practices:
-
Use sub-workflows for reusable logic
-
Pass minimal data between workflows
-
Set waitForSubWorkflow based on needs
-
Use workflow tags for organization
HTTP Request Patterns
REST API Call
{ "name": "API Request", "type": "n8n-nodes-base.httpRequest", "parameters": { "method": "POST", "url": "https://api.example.com/v1/resource", "authentication": "predefinedCredentialType", "nodeCredentialType": "httpHeaderAuth", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "application/json" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "data", "value": "={{ JSON.stringify($json) }}" } ] }, "options": { "timeout": 30000, "response": { "response": { "fullResponse": false, "responseFormat": "json" } } } } }
Pagination Pattern
// Code node for API pagination const allResults = []; let page = 1; let hasMore = true;
while (hasMore) {
const response = await this.helpers.httpRequest({
method: 'GET',
url: https://api.example.com/items?page=${page}&limit=100,
headers: {
'Authorization': Bearer ${$env.API_TOKEN}
}
});
allResults.push(...response.data); hasMore = response.hasNextPage; page++;
// Rate limiting await new Promise(r => setTimeout(r, 100)); }
return allResults.map(item => ({ json: item }));
Credential Management
Environment Variables
// Access in expressions {{ $env.API_KEY }} {{ $env.DATABASE_URL }}
// Access in Code node const apiKey = $env.API_KEY;
Credential Types
Type Use Case
httpBasicAuth
Basic authentication
httpHeaderAuth
API key in header
oAuth2Api
OAuth 2.0 flows
httpQueryAuth
API key in query string
Self-Hosting Patterns
Docker Compose
version: '3.8'
services: n8n: image: n8nio/n8n:latest restart: unless-stopped ports: - "5678:5678" environment: - N8N_BASIC_AUTH_ACTIVE=true - N8N_BASIC_AUTH_USER=${N8N_USER} - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD} - N8N_HOST=${N8N_HOST} - N8N_PORT=5678 - N8N_PROTOCOL=https - NODE_ENV=production - WEBHOOK_URL=https://${N8N_HOST}/ - GENERIC_TIMEZONE=UTC - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY} - DB_TYPE=postgresdb - DB_POSTGRESDB_HOST=postgres - DB_POSTGRESDB_PORT=5432 - DB_POSTGRESDB_DATABASE=n8n - DB_POSTGRESDB_USER=${POSTGRES_USER} - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD} - EXECUTIONS_DATA_PRUNE=true - EXECUTIONS_DATA_MAX_AGE=168 volumes: - n8n_data:/home/node/.n8n depends_on: - postgres
postgres: image: postgres:15 restart: unless-stopped environment: - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=n8n volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] interval: 10s timeout: 5s retries: 5
volumes: n8n_data: postgres_data:
Environment Variables (.env)
n8n Configuration
N8N_HOST=n8n.example.com N8N_USER=admin N8N_PASSWORD=secure-password-here N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)
Database
POSTGRES_USER=n8n POSTGRES_PASSWORD=secure-db-password
Optional: Queue mode for scaling
EXECUTIONS_MODE=queue QUEUE_BULL_REDIS_HOST=redis
Queue Mode (Scaling)
docker-compose.queue.yml
services: n8n: environment: - EXECUTIONS_MODE=queue - QUEUE_BULL_REDIS_HOST=redis - QUEUE_HEALTH_CHECK_ACTIVE=true
n8n-worker: image: n8nio/n8n:latest command: worker environment: - EXECUTIONS_MODE=queue - QUEUE_BULL_REDIS_HOST=redis deploy: replicas: 3
redis: image: redis:7-alpine volumes: - redis_data:/data
Common Workflow Templates
Webhook to Database
{ "name": "Webhook to Database", "nodes": [ { "name": "Webhook", "type": "n8n-nodes-base.webhook", "parameters": { "httpMethod": "POST", "path": "ingest", "responseMode": "responseNode" } }, { "name": "Validate", "type": "n8n-nodes-base.if", "parameters": { "conditions": { "conditions": [ { "leftValue": "={{ $json.id }}", "rightValue": "", "operator": { "type": "string", "operation": "notEmpty" } } ] } } }, { "name": "Insert", "type": "n8n-nodes-base.postgres", "parameters": { "operation": "insert", "table": "events", "columns": "id,type,data,created_at" } }, { "name": "Success Response", "type": "n8n-nodes-base.respondToWebhook", "parameters": { "respondWith": "json", "responseBody": "={{ { "success": true, "id": $json.id } }}" } } ] }
Scheduled Sync
{ "name": "Daily Data Sync", "nodes": [ { "name": "Schedule", "type": "n8n-nodes-base.scheduleTrigger", "parameters": { "rule": { "interval": [{ "field": "cronExpression", "expression": "0 2 * * *" }] } } }, { "name": "Fetch Source", "type": "n8n-nodes-base.httpRequest", "parameters": { "url": "https://api.source.com/data", "authentication": "predefinedCredentialType" } }, { "name": "Transform", "type": "n8n-nodes-base.code", "parameters": { "jsCode": "return $input.all().map(item => ({ json: { ...item.json, synced_at: new Date().toISOString() } }));" } }, { "name": "Upsert Destination", "type": "n8n-nodes-base.postgres", "parameters": { "operation": "upsert", "table": "synced_data" } } ] }
Event-Driven Notification
{ "name": "Alert Pipeline", "nodes": [ { "name": "Webhook", "type": "n8n-nodes-base.webhook", "parameters": { "path": "alert" } }, { "name": "Route by Severity", "type": "n8n-nodes-base.switch", "parameters": { "rules": { "values": [ { "outputKey": "critical", "conditions": { "conditions": [{ "leftValue": "={{ $json.severity }}", "rightValue": "critical" }] } }, { "outputKey": "warning", "conditions": { "conditions": [{ "leftValue": "={{ $json.severity }}", "rightValue": "warning" }] } } ] } } }, { "name": "Page On-Call", "type": "n8n-nodes-base.pagerDuty" }, { "name": "Slack Alert", "type": "n8n-nodes-base.slack" } ] }
Expression Cheat Sheet
Expression Description
{{ $json.field }}
Access field from current item
{{ $json["field-name"] }}
Access field with special chars
{{ $('NodeName').item.json.field }}
Access data from specific node
{{ $input.first().json }}
First input item
{{ $input.all() }}
All input items
{{ $env.VAR_NAME }}
Environment variable
{{ $now }}
Current datetime
{{ $today }}
Current date
{{ $runIndex }}
Current execution run index
{{ $itemIndex }}
Current item index
{{ $workflow.id }}
Workflow ID
{{ $execution.id }}
Execution ID
Luxon DateTime Examples
// n8n uses Luxon for dates {{ $now.toISO() }} // ISO format {{ $now.toFormat('yyyy-MM-dd') }} // Custom format {{ $now.plus({ days: 7 }).toISO() }} // Add 7 days {{ $now.startOf('month').toISO() }} // Start of month {{ DateTime.fromISO($json.date) }} // Parse ISO string
Best Practices
-
Naming: Use descriptive node names (verb + noun)
-
Error Handling: Always add error workflows
-
Credentials: Never hardcode secrets
-
Batching: Use splitInBatches for large datasets
-
Timeouts: Set appropriate timeouts on HTTP nodes
-
Logging: Use console.log in Code nodes for debugging
-
Testing: Use manual execution before activating
-
Version Control: Export workflows as JSON to git
-
Documentation: Add sticky notes for complex logic
-
Modular Design: Use sub-workflows for reusability
Debugging Tips
// In Code node - log to n8n console console.log('Debug:', JSON.stringify($json, null, 2));
// Return debug info return [{ json: { debug: true, input: $json, env: $env.NODE_ENV, timestamp: new Date().toISOString() } }];
Resources
-
n8n Documentation
-
n8n Community
-
Workflow Templates
-
Node Reference