N8n
Build self-hosted and cloud workflow automations with n8n using nodes, expressions, webhooks, and code nodes.
You are an expert in n8n workflow automation, building production workflows with triggers, code nodes, credential management, and custom node development. You design workflows for both self-hosted and n8n Cloud deployments. ## Key Points 1. **Storing credentials in Code node strings** - Always use n8n's credential system. It encrypts at rest and supports rotation without editing workflows. 2. **Running Code nodes in "Run Once per Item" when processing arrays** - Use "Run Once for All Items" mode and handle iteration yourself for better performance. 3. **Ignoring execution data pruning** - Self-hosted n8n stores all execution data by default. Configure `EXECUTIONS_DATA_PRUNE=true` and `EXECUTIONS_DATA_MAX_AGE` to prevent database bloat. 4. **Building long linear chains without error handling** - Attach error workflows and use IF nodes to validate data between critical steps. - Self-hosting workflow automation for data sovereignty or air-gapped environments - Building developer-centric automations where Code nodes provide flexibility beyond visual builders - Creating custom integration nodes for internal services not covered by built-in nodes - Running workflows that process sensitive data requiring on-premise credential management - Prototyping automation pipelines with the visual editor before migrating to production infrastructure
skilldb get automation-workflow-services-skills/N8nFull skill: 228 linesn8n Workflow Automation
You are an expert in n8n workflow automation, building production workflows with triggers, code nodes, credential management, and custom node development. You design workflows for both self-hosted and n8n Cloud deployments.
Core Philosophy
Node Composition Over Monoliths
Each n8n node should perform one focused task. Chain nodes to build pipelines. Use the Code node for data transformation logic but keep API calls in dedicated integration nodes where available.
Expression-Driven Data Access
n8n uses a powerful expression system to reference data from previous nodes. Master $json, $input, $node, and $execution variables. Use expressions in node parameters instead of routing data through unnecessary Set nodes.
Self-Hosted Control
n8n's open-source model means you control the infrastructure, credentials, and execution environment. Leverage this for compliance requirements, air-gapped networks, and custom node development.
Setup
# Docker self-hosted deployment
docker run -d --name n8n \
-p 5678:5678 \
-v n8n_data:/home/node/.n8n \
-e N8N_BASIC_AUTH_ACTIVE=true \
-e N8N_BASIC_AUTH_USER=admin \
-e N8N_BASIC_AUTH_PASSWORD=changeme \
-e EXECUTIONS_DATA_PRUNE=true \
-e EXECUTIONS_DATA_MAX_AGE=168 \
n8nio/n8n:latest
# npm global install for development
npm install -g n8n
# Create custom node project
npx n8n-node-dev new
// n8n API client for workflow management
const N8N_API = "http://localhost:5678/api/v1";
const headers = { "X-N8N-API-KEY": process.env.N8N_API_KEY };
// List all workflows
const response = await fetch(`${N8N_API}/workflows`, { headers });
const { data: workflows } = await response.json();
Key Patterns
Use Webhook Nodes for Real-Time Triggers
// Do: Configure webhook trigger with validation
// Webhook node settings:
// - HTTP Method: POST
// - Path: /order-received
// - Authentication: Header Auth
// - Response Mode: "Last Node" for synchronous processing
// Access webhook data in subsequent nodes:
// Expression: {{ $json.body.orderId }}
// Don't: Poll APIs with Schedule Trigger when webhooks are available
Leverage Code Nodes for Transformation
// Do: Use Code node for data mapping
// Code node (Run Once for All Items mode):
const items = $input.all();
return items.map((item) => ({
json: {
fullName: `${item.json.firstName} ${item.json.lastName}`,
email: item.json.email.toLowerCase(),
segment: item.json.totalSpend > 1000 ? "premium" : "standard",
},
}));
// Don't: Use multiple Set nodes to achieve what one Code node does cleanly
Configure Error Workflows
// Do: Set up a dedicated error workflow
// In workflow settings: Error Workflow = "Error Handler Workflow"
// Error handler receives: {{ $execution.id }}, {{ $execution.error.message }}
// Error handler workflow nodes:
// 1. Error Trigger node
// 2. Slack node: post to #alerts channel
// 3. HTTP node: log to incident tracking system
// Don't: Leave workflows without error handling in production
Common Patterns
API Integration with Pagination
// Code node: paginated API fetch
const allResults: any[] = [];
let page = 1;
let hasMore = true;
while (hasMore) {
const response = await fetch(
`https://api.example.com/records?page=${page}&limit=100`,
{ headers: { Authorization: `Bearer ${$credentials.apiToken}` } }
);
const data = await response.json();
allResults.push(...data.records);
hasMore = data.hasMore;
page++;
}
return allResults.map((record) => ({ json: record }));
Split and Merge Workflow
// Workflow structure:
// 1. Webhook Trigger
// 2. Switch node: route by {{ $json.type }}
// - Output 0: type === "order" => Order processing branch
// - Output 1: type === "refund" => Refund processing branch
// - Output 2: fallback => Log unknown type
// 3. Merge node: combine results from branches
// 4. Respond to Webhook node: return combined result
// Switch node expression:
// Routing Rules:
// Value 1: {{ $json.type }}
// Operation: equals
// Value 2: "order"
Custom Node Development
// nodes/MyService/MyService.node.ts
import { INodeType, INodeTypeDescription, IExecuteFunctions } from "n8n-workflow";
export class MyService implements INodeType {
description: INodeTypeDescription = {
displayName: "My Service",
name: "myService",
group: ["transform"],
version: 1,
description: "Interact with My Service API",
defaults: { name: "My Service" },
inputs: ["main"],
outputs: ["main"],
credentials: [{ name: "myServiceApi", required: true }],
properties: [
{
displayName: "Operation",
name: "operation",
type: "options",
options: [
{ name: "Get Record", value: "get" },
{ name: "Create Record", value: "create" },
],
default: "get",
},
{
displayName: "Record ID",
name: "recordId",
type: "string",
default: "",
displayOptions: { show: { operation: ["get"] } },
},
],
};
async execute(this: IExecuteFunctions) {
const items = this.getInputData();
const operation = this.getNodeParameter("operation", 0);
const credentials = await this.getCredentials("myServiceApi");
const results = [];
for (let i = 0; i < items.length; i++) {
if (operation === "get") {
const recordId = this.getNodeParameter("recordId", i) as string;
const response = await this.helpers.httpRequest({
url: `https://api.example.com/records/${recordId}`,
headers: { Authorization: `Bearer ${credentials.apiKey}` },
});
results.push({ json: response });
}
}
return [results];
}
}
Scheduled Workflow with Deduplication
// 1. Schedule Trigger: every 15 minutes
// 2. HTTP Request: GET /api/events?since=last_check
// 3. Code node: filter already-processed IDs
// const seen = $workflow.staticData.processedIds || [];
// const newItems = $input.all().filter(i => !seen.includes(i.json.id));
// const newIds = newItems.map(i => i.json.id);
// $workflow.staticData.processedIds = [...seen, ...newIds].slice(-1000);
// return newItems;
// 4. IF node: check {{ $json }} is not empty
// 5. Processing nodes
Anti-Patterns
- Storing credentials in Code node strings - Always use n8n's credential system. It encrypts at rest and supports rotation without editing workflows.
- Running Code nodes in "Run Once per Item" when processing arrays - Use "Run Once for All Items" mode and handle iteration yourself for better performance.
- Ignoring execution data pruning - Self-hosted n8n stores all execution data by default. Configure
EXECUTIONS_DATA_PRUNE=trueandEXECUTIONS_DATA_MAX_AGEto prevent database bloat. - Building long linear chains without error handling - Attach error workflows and use IF nodes to validate data between critical steps.
When to Use
- Self-hosting workflow automation for data sovereignty or air-gapped environments
- Building developer-centric automations where Code nodes provide flexibility beyond visual builders
- Creating custom integration nodes for internal services not covered by built-in nodes
- Running workflows that process sensitive data requiring on-premise credential management
- Prototyping automation pipelines with the visual editor before migrating to production infrastructure
Install this skill directly: skilldb add automation-workflow-services-skills
Related Skills
Make Integromat
Build and manage Make (formerly Integromat) scenarios using modules, routers, webhooks, and data stores.
Pipedream
Build serverless event-driven workflows with Pipedream using triggers, Node.js/Python steps, and data stores.
Retool
Build internal tools with Retool using queries, components, transformers, and workflows.
Superblocks
Build internal tools and workflows with Superblocks using API integrations, UI components, scheduled jobs, and permissions.
Val Town
Create serverless functions with Val Town using vals for HTTP handlers, cron jobs, email handlers, and SQLite storage.
Windmill
Build scripts, flows, and apps with Windmill using TypeScript and Python runtimes.