Loading...
Loading...
Design and implement n8n workflow automations with best practices
npx skill4agent add mindmorass/reflex n8n-patternsBuild robust workflow automations with n8n - the open-source workflow automation tool.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Trigger │───▶│ Node │───▶│ Output │
│ (Start) │ │ (Process) │ │ (Action) │
└─────────────┘ └─────────────┘ └─────────────┘| 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 |
{
"nodes": [
{
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"parameters": {
"httpMethod": "POST",
"path": "my-webhook",
"responseMode": "responseNode",
"options": {
"rawBody": true
}
}
}
]
}responseNoderawBody{
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 9 * * 1-5"
}
]
}
}
}0 * * * *0 9 * * 1-50 0 * * 0*/15 * * * *{
"name": "GitHub Trigger",
"type": "n8n-nodes-base.githubTrigger",
"parameters": {
"owner": "{{$env.GITHUB_OWNER}}",
"repository": "{{$env.GITHUB_REPO}}",
"events": ["issues", "pull_request"]
}
}{
"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"
}
]
}
}
}// 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;# 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{
"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"
}
}
}{
"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"
}
}{
"name": "Loop Over Items",
"type": "n8n-nodes-base.splitInBatches",
"parameters": {
"batchSize": 10,
"options": {
"reset": false
}
}
}{
"name": "Merge Results",
"type": "n8n-nodes-base.merge",
"parameters": {
"mode": "combine",
"mergeByFields": {
"values": [
{
"field1": "id",
"field2": "userId"
}
]
},
"options": {}
}
}{
"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 }}"
}
}
]
}{
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://api.example.com/data",
"options": {}
},
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 1000
}{
"name": "Validation Failed",
"type": "n8n-nodes-base.stopAndError",
"parameters": {
"errorMessage": "Invalid input: {{ $json.error }}"
}
}{
"name": "Process Order",
"type": "n8n-nodes-base.executeWorkflow",
"parameters": {
"source": "database",
"workflowId": "order-processing-workflow-id",
"mode": "each",
"options": {
"waitForSubWorkflow": true
}
}
}waitForSubWorkflow{
"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"
}
}
}
}
}// 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 }));// Access in expressions
{{ $env.API_KEY }}
{{ $env.DATABASE_URL }}
// Access in Code node
const apiKey = $env.API_KEY;| Type | Use Case |
|---|---|
| Basic authentication |
| API key in header |
| OAuth 2.0 flows |
| API key in query string |
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:# 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# 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{
"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 } }}"
}
}
]
}{
"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"
}
}
]
}{
"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 | Description |
|---|---|
| Access field from current item |
| Access field with special chars |
| Access data from specific node |
| First input item |
| All input items |
| Environment variable |
| Current datetime |
| Current date |
| Current execution run index |
| Current item index |
| Workflow ID |
| Execution ID |
// 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 stringsplitInBatchesconsole.log// 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()
}
}];