Files
2025-11-29 18:17:17 +08:00

1111 lines
28 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Common Patterns - JavaScript Code Node
Production-tested patterns for n8n Code nodes. These patterns are proven in real workflows.
---
## Overview
This guide covers the 10 most useful Code node patterns for n8n workflows. Each pattern includes:
- **Use Case**: When to use this pattern
- **Key Techniques**: Important coding techniques demonstrated
- **Complete Example**: Working code you can adapt
- **Variations**: Common modifications
**Pattern Categories:**
- Data Aggregation (Patterns 1, 5, 10)
- Content Processing (Patterns 2, 3)
- Data Validation & Comparison (Patterns 4)
- Data Transformation (Patterns 5, 6, 7)
- Output Formatting (Pattern 8)
- Filtering & Ranking (Pattern 9)
---
## Pattern 1: Multi-Source Data Aggregation
**Use Case**: Combining data from multiple APIs, RSS feeds, webhooks, or databases
**When to use:**
- Collecting data from multiple services
- Normalizing different API response formats
- Merging data sources into unified structure
- Building aggregated reports
**Key Techniques**: Loop iteration, conditional parsing, data normalization
### Complete Example
```javascript
// Process and structure data collected from multiple sources
const allItems = $input.all();
let processedArticles = [];
// Handle different source formats
for (const item of allItems) {
const sourceName = item.json.name || 'Unknown';
const sourceData = item.json;
// Parse source-specific structure - Hacker News format
if (sourceName === 'Hacker News' && sourceData.hits) {
for (const hit of sourceData.hits) {
processedArticles.push({
title: hit.title,
url: hit.url,
summary: hit.story_text || 'No summary',
source: 'Hacker News',
score: hit.points || 0,
fetchedAt: new Date().toISOString()
});
}
}
// Parse source-specific structure - Reddit format
else if (sourceName === 'Reddit' && sourceData.data?.children) {
for (const post of sourceData.data.children) {
processedArticles.push({
title: post.data.title,
url: post.data.url,
summary: post.data.selftext || 'No summary',
source: 'Reddit',
score: post.data.score || 0,
fetchedAt: new Date().toISOString()
});
}
}
// Parse source-specific structure - RSS feed format
else if (sourceName === 'RSS' && sourceData.items) {
for (const rssItem of sourceData.items) {
processedArticles.push({
title: rssItem.title,
url: rssItem.link,
summary: rssItem.description || 'No summary',
source: 'RSS Feed',
score: 0,
fetchedAt: new Date().toISOString()
});
}
}
}
// Sort by score (highest first)
processedArticles.sort((a, b) => b.score - a.score);
return processedArticles.map(article => ({json: article}));
```
### Variations
```javascript
// Variation 1: Add source weighting
for (const article of processedArticles) {
const weights = {
'Hacker News': 1.5,
'Reddit': 1.0,
'RSS Feed': 0.8
};
article.weightedScore = article.score * (weights[article.source] || 1.0);
}
// Variation 2: Filter by minimum score
processedArticles = processedArticles.filter(article => article.score >= 10);
// Variation 3: Deduplicate by URL
const seen = new Set();
processedArticles = processedArticles.filter(article => {
if (seen.has(article.url)) {
return false;
}
seen.add(article.url);
return true;
});
```
---
## Pattern 2: Regex Filtering & Pattern Matching
**Use Case**: Content analysis, keyword extraction, mention tracking, text parsing
**When to use:**
- Extracting mentions or tags from text
- Finding patterns in unstructured data
- Counting keyword occurrences
- Validating formats (emails, phone numbers)
**Key Techniques**: Regex matching, object aggregation, sorting/ranking
### Complete Example
```javascript
// Extract and track mentions using regex patterns
const etfPattern = /\b([A-Z]{2,5})\b/g;
const knownETFs = ['VOO', 'VTI', 'VT', 'SCHD', 'QYLD', 'VXUS', 'SPY', 'QQQ'];
const etfMentions = {};
for (const item of $input.all()) {
const data = item.json.data;
// Skip if no data or children
if (!data?.children) continue;
for (const post of data.children) {
// Combine title and body text
const title = post.data.title || '';
const body = post.data.selftext || '';
const combinedText = (title + ' ' + body).toUpperCase();
// Find all matches
const matches = combinedText.match(etfPattern);
if (matches) {
for (const match of matches) {
// Only count known ETFs
if (knownETFs.includes(match)) {
if (!etfMentions[match]) {
etfMentions[match] = {
count: 0,
totalScore: 0,
posts: []
};
}
etfMentions[match].count++;
etfMentions[match].totalScore += post.data.score || 0;
etfMentions[match].posts.push({
title: post.data.title,
url: post.data.url,
score: post.data.score
});
}
}
}
}
}
// Convert to array and sort by mention count
return Object.entries(etfMentions)
.map(([etf, data]) => ({
json: {
etf,
mentions: data.count,
totalScore: data.totalScore,
averageScore: data.totalScore / data.count,
topPosts: data.posts
.sort((a, b) => b.score - a.score)
.slice(0, 3)
}
}))
.sort((a, b) => b.json.mentions - a.json.mentions);
```
### Variations
```javascript
// Variation 1: Email extraction
const emailPattern = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g;
const emails = text.match(emailPattern) || [];
// Variation 2: Phone number extraction
const phonePattern = /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g;
const phones = text.match(phonePattern) || [];
// Variation 3: Hashtag extraction
const hashtagPattern = /#(\w+)/g;
const hashtags = [];
let match;
while ((match = hashtagPattern.exec(text)) !== null) {
hashtags.push(match[1]);
}
// Variation 4: URL extraction
const urlPattern = /https?:\/\/[^\s]+/g;
const urls = text.match(urlPattern) || [];
```
---
## Pattern 3: Markdown Parsing & Structured Data Extraction
**Use Case**: Parsing formatted text, extracting structured fields, content transformation
**When to use:**
- Parsing markdown or HTML
- Extracting data from structured text
- Converting formatted content to JSON
- Processing documentation or articles
**Key Techniques**: Regex grouping, helper functions, data normalization, while loops for iteration
### Complete Example
```javascript
// Parse markdown and extract structured information
const markdown = $input.first().json.data.markdown;
const adRegex = /##\s*(.*?)\n(.*?)(?=\n##|\n---|$)/gs;
const ads = [];
let match;
// Helper function to parse time strings to minutes
function parseTimeToMinutes(timeStr) {
if (!timeStr) return 999999; // Sort unparseable times last
const hourMatch = timeStr.match(/(\d+)\s*hour/);
const dayMatch = timeStr.match(/(\d+)\s*day/);
const minMatch = timeStr.match(/(\d+)\s*min/);
let totalMinutes = 0;
if (dayMatch) totalMinutes += parseInt(dayMatch[1]) * 1440; // 24 * 60
if (hourMatch) totalMinutes += parseInt(hourMatch[1]) * 60;
if (minMatch) totalMinutes += parseInt(minMatch[1]);
return totalMinutes;
}
// Extract all job postings from markdown
while ((match = adRegex.exec(markdown)) !== null) {
const title = match[1]?.trim() || 'No title';
const content = match[2]?.trim() || '';
// Extract structured fields from content
const districtMatch = content.match(/\*\*District:\*\*\s*(.*?)(?:\n|$)/);
const salaryMatch = content.match(/\*\*Salary:\*\*\s*(.*?)(?:\n|$)/);
const timeMatch = content.match(/Posted:\s*(.*?)\*/);
ads.push({
title: title,
district: districtMatch?.[1].trim() || 'Unknown',
salary: salaryMatch?.[1].trim() || 'Not specified',
postedTimeAgo: timeMatch?.[1] || 'Unknown',
timeInMinutes: parseTimeToMinutes(timeMatch?.[1]),
fullContent: content,
extractedAt: new Date().toISOString()
});
}
// Sort by recency (posted time)
ads.sort((a, b) => a.timeInMinutes - b.timeInMinutes);
return ads.map(ad => ({json: ad}));
```
### Variations
```javascript
// Variation 1: Parse HTML table to JSON
const tableRegex = /<tr>(.*?)<\/tr>/gs;
const cellRegex = /<td>(.*?)<\/td>/g;
const rows = [];
let tableMatch;
while ((tableMatch = tableRegex.exec(htmlTable)) !== null) {
const cells = [];
let cellMatch;
while ((cellMatch = cellRegex.exec(tableMatch[1])) !== null) {
cells.push(cellMatch[1].trim());
}
if (cells.length > 0) {
rows.push(cells);
}
}
// Variation 2: Extract code blocks from markdown
const codeBlockRegex = /```(\w+)?\n(.*?)```/gs;
const codeBlocks = [];
while ((match = codeBlockRegex.exec(markdown)) !== null) {
codeBlocks.push({
language: match[1] || 'plain',
code: match[2].trim()
});
}
// Variation 3: Parse YAML frontmatter
const frontmatterRegex = /^---\n(.*?)\n---/s;
const frontmatterMatch = content.match(frontmatterRegex);
if (frontmatterMatch) {
const yamlLines = frontmatterMatch[1].split('\n');
const metadata = {};
for (const line of yamlLines) {
const [key, ...valueParts] = line.split(':');
if (key && valueParts.length > 0) {
metadata[key.trim()] = valueParts.join(':').trim();
}
}
}
```
---
## Pattern 4: JSON Comparison & Validation
**Use Case**: Workflow versioning, configuration validation, change detection, data integrity
**When to use:**
- Comparing two versions of data
- Detecting changes in configurations
- Validating data consistency
- Checking for differences
**Key Techniques**: JSON ordering, base64 decoding, deep comparison, object manipulation
### Complete Example
```javascript
// Compare and validate JSON objects from different sources
const orderJsonKeys = (jsonObj) => {
const ordered = {};
Object.keys(jsonObj).sort().forEach(key => {
ordered[key] = jsonObj[key];
});
return ordered;
};
const allItems = $input.all();
// Assume first item is base64-encoded original, second is current
const origWorkflow = JSON.parse(
Buffer.from(allItems[0].json.content, 'base64').toString()
);
const currentWorkflow = allItems[1].json;
// Order keys for consistent comparison
const orderedOriginal = orderJsonKeys(origWorkflow);
const orderedCurrent = orderJsonKeys(currentWorkflow);
// Deep comparison
const isSame = JSON.stringify(orderedOriginal) === JSON.stringify(orderedCurrent);
// Find differences
const differences = [];
for (const key of Object.keys(orderedOriginal)) {
if (JSON.stringify(orderedOriginal[key]) !== JSON.stringify(orderedCurrent[key])) {
differences.push({
field: key,
original: orderedOriginal[key],
current: orderedCurrent[key]
});
}
}
// Check for new keys
for (const key of Object.keys(orderedCurrent)) {
if (!(key in orderedOriginal)) {
differences.push({
field: key,
original: null,
current: orderedCurrent[key],
status: 'new'
});
}
}
return [{
json: {
identical: isSame,
differenceCount: differences.length,
differences: differences,
original: orderedOriginal,
current: orderedCurrent,
comparedAt: new Date().toISOString()
}
}];
```
### Variations
```javascript
// Variation 1: Simple equality check
const isEqual = JSON.stringify(obj1) === JSON.stringify(obj2);
// Variation 2: Deep diff with detailed changes
function deepDiff(obj1, obj2, path = '') {
const changes = [];
for (const key in obj1) {
const currentPath = path ? `${path}.${key}` : key;
if (!(key in obj2)) {
changes.push({type: 'removed', path: currentPath, value: obj1[key]});
} else if (typeof obj1[key] === 'object' && typeof obj2[key] === 'object') {
changes.push(...deepDiff(obj1[key], obj2[key], currentPath));
} else if (obj1[key] !== obj2[key]) {
changes.push({
type: 'modified',
path: currentPath,
from: obj1[key],
to: obj2[key]
});
}
}
for (const key in obj2) {
if (!(key in obj1)) {
const currentPath = path ? `${path}.${key}` : key;
changes.push({type: 'added', path: currentPath, value: obj2[key]});
}
}
return changes;
}
// Variation 3: Schema validation
function validateSchema(data, schema) {
const errors = [];
for (const field of schema.required || []) {
if (!(field in data)) {
errors.push(`Missing required field: ${field}`);
}
}
for (const [field, type] of Object.entries(schema.types || {})) {
if (field in data && typeof data[field] !== type) {
errors.push(`Field ${field} should be ${type}, got ${typeof data[field]}`);
}
}
return {
valid: errors.length === 0,
errors
};
}
```
---
## Pattern 5: CRM Data Transformation
**Use Case**: Lead enrichment, data normalization, API preparation, form data processing
**When to use:**
- Processing form submissions
- Preparing data for CRM APIs
- Normalizing contact information
- Enriching lead data
**Key Techniques**: Object destructuring, data mapping, format conversion, field splitting
### Complete Example
```javascript
// Transform form data into CRM-compatible format
const item = $input.all()[0];
const {
name,
email,
phone,
company,
course_interest,
message,
timestamp
} = item.json;
// Split name into first and last
const nameParts = name.split(' ');
const firstName = nameParts[0] || '';
const lastName = nameParts.slice(1).join(' ') || 'Unknown';
// Format phone number
const cleanPhone = phone.replace(/[^\d]/g, ''); // Remove non-digits
// Build CRM data structure
const crmData = {
data: {
type: 'Contact',
attributes: {
first_name: firstName,
last_name: lastName,
email1: email,
phone_work: cleanPhone,
account_name: company,
description: `Course Interest: ${course_interest}\n\nMessage: ${message}\n\nSubmitted: ${timestamp}`,
lead_source: 'Website Form',
status: 'New'
}
},
metadata: {
original_submission: timestamp,
processed_at: new Date().toISOString()
}
};
return [{
json: {
...item.json,
crmData,
processed: true
}
}];
```
### Variations
```javascript
// Variation 1: Multiple contact processing
const contacts = $input.all();
return contacts.map(item => {
const data = item.json;
const [firstName, ...lastNameParts] = data.name.split(' ');
return {
json: {
firstName,
lastName: lastNameParts.join(' ') || 'Unknown',
email: data.email.toLowerCase(),
phone: data.phone.replace(/[^\d]/g, ''),
tags: [data.source, data.interest_level].filter(Boolean)
}
};
});
// Variation 2: Field validation and normalization
function normalizePContact(raw) {
return {
first_name: raw.firstName?.trim() || '',
last_name: raw.lastName?.trim() || 'Unknown',
email: raw.email?.toLowerCase().trim() || '',
phone: raw.phone?.replace(/[^\d]/g, '') || '',
company: raw.company?.trim() || 'Unknown',
title: raw.title?.trim() || '',
valid: Boolean(raw.email && raw.firstName)
};
}
// Variation 3: Lead scoring
function calculateLeadScore(data) {
let score = 0;
if (data.email) score += 10;
if (data.phone) score += 10;
if (data.company) score += 15;
if (data.title?.toLowerCase().includes('director')) score += 20;
if (data.title?.toLowerCase().includes('manager')) score += 15;
if (data.message?.length > 100) score += 10;
return score;
}
```
---
## Pattern 6: Release Information Processing
**Use Case**: Version management, changelog parsing, release notes generation, GitHub API processing
**When to use:**
- Processing GitHub releases
- Filtering stable versions
- Generating changelog summaries
- Extracting version information
**Key Techniques**: Array filtering, conditional field extraction, date formatting, string manipulation
### Complete Example
```javascript
// Extract and filter stable releases from GitHub API
const allReleases = $input.first().json;
const stableReleases = allReleases
.filter(release => !release.prerelease && !release.draft)
.slice(0, 10)
.map(release => {
// Extract highlights section from changelog
const body = release.body || '';
let highlights = 'No highlights available';
if (body.includes('## Highlights:')) {
highlights = body.split('## Highlights:')[1]?.split('##')[0]?.trim();
} else {
// Fallback to first 500 chars
highlights = body.substring(0, 500) + '...';
}
return {
tag: release.tag_name,
name: release.name,
published: release.published_at,
publishedDate: new Date(release.published_at).toLocaleDateString(),
author: release.author.login,
url: release.html_url,
changelog: body,
highlights: highlights,
assetCount: release.assets.length,
assets: release.assets.map(asset => ({
name: asset.name,
size: asset.size,
downloadCount: asset.download_count,
downloadUrl: asset.browser_download_url
}))
};
});
return stableReleases.map(release => ({json: release}));
```
### Variations
```javascript
// Variation 1: Version comparison
function compareVersions(v1, v2) {
const parts1 = v1.replace('v', '').split('.').map(Number);
const parts2 = v2.replace('v', '').split('.').map(Number);
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
const num1 = parts1[i] || 0;
const num2 = parts2[i] || 0;
if (num1 > num2) return 1;
if (num1 < num2) return -1;
}
return 0;
}
// Variation 2: Breaking change detection
function hasBreakingChanges(changelog) {
const breakingKeywords = [
'BREAKING CHANGE',
'breaking change',
'BC:',
'💥'
];
return breakingKeywords.some(keyword => changelog.includes(keyword));
}
// Variation 3: Extract version numbers
const versionPattern = /v?(\d+)\.(\d+)\.(\d+)/;
const match = tagName.match(versionPattern);
if (match) {
const [_, major, minor, patch] = match;
const version = {major: parseInt(major), minor: parseInt(minor), patch: parseInt(patch)};
}
```
---
## Pattern 7: Array Transformation with Context
**Use Case**: Quick data transformation, field mapping, adding computed fields
**When to use:**
- Transforming arrays with additional context
- Adding calculated fields
- Simplifying complex objects
- Pluralization logic
**Key Techniques**: Array methods chaining, ternary operators, computed properties
### Complete Example
```javascript
// Transform releases with contextual information
const releases = $input.first().json
.filter(release => !release.prerelease && !release.draft)
.slice(0, 10)
.map(release => ({
version: release.tag_name,
assetCount: release.assets.length,
assetsCountText: release.assets.length === 1 ? 'file' : 'files',
downloadUrl: release.html_url,
isRecent: new Date(release.published_at) > new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
age: Math.floor((Date.now() - new Date(release.published_at)) / (24 * 60 * 60 * 1000)),
ageText: `${Math.floor((Date.now() - new Date(release.published_at)) / (24 * 60 * 60 * 1000))} days ago`
}));
return releases.map(release => ({json: release}));
```
### Variations
```javascript
// Variation 1: Add ranking
const items = $input.all()
.sort((a, b) => b.json.score - a.json.score)
.map((item, index) => ({
json: {
...item.json,
rank: index + 1,
medal: index < 3 ? ['🥇', '🥈', '🥉'][index] : ''
}
}));
// Variation 2: Add percentage calculations
const total = $input.all().reduce((sum, item) => sum + item.json.value, 0);
const itemsWithPercentage = $input.all().map(item => ({
json: {
...item.json,
percentage: ((item.json.value / total) * 100).toFixed(2) + '%'
}
}));
// Variation 3: Add category labels
const categorize = (value) => {
if (value > 100) return 'High';
if (value > 50) return 'Medium';
return 'Low';
};
const categorized = $input.all().map(item => ({
json: {
...item.json,
category: categorize(item.json.value)
}
}));
```
---
## Pattern 8: Slack Block Kit Formatting
**Use Case**: Chat notifications, rich message formatting, interactive messages
**When to use:**
- Sending formatted Slack messages
- Creating interactive notifications
- Building rich content for chat platforms
- Status reports and alerts
**Key Techniques**: Template literals, nested objects, Block Kit syntax, date formatting
### Complete Example
```javascript
// Create Slack-formatted message with structured blocks
const date = new Date().toISOString().split('T')[0];
const data = $input.first().json;
return [{
json: {
text: `Daily Report - ${date}`, // Fallback text
blocks: [
{
type: "header",
text: {
type: "plain_text",
text: `📊 Daily Security Report - ${date}`
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: `*Status:* ${data.status === 'ok' ? '✅ All Clear' : '⚠️ Issues Detected'}\n*Alerts:* ${data.alertCount || 0}\n*Updated:* ${new Date().toLocaleString()}`
}
},
{
type: "divider"
},
{
type: "section",
fields: [
{
type: "mrkdwn",
text: `*Failed Logins:*\n${data.failedLogins || 0}`
},
{
type: "mrkdwn",
text: `*API Errors:*\n${data.apiErrors || 0}`
},
{
type: "mrkdwn",
text: `*Uptime:*\n${data.uptime || '100%'}`
},
{
type: "mrkdwn",
text: `*Response Time:*\n${data.avgResponseTime || 'N/A'}ms`
}
]
},
{
type: "context",
elements: [{
type: "mrkdwn",
text: `Report generated automatically by n8n workflow`
}]
}
]
}
}];
```
### Variations
```javascript
// Variation 1: Interactive buttons
const blocksWithButtons = [
{
type: "section",
text: {
type: "mrkdwn",
text: "Would you like to approve this request?"
},
accessory: {
type: "button",
text: {
type: "plain_text",
text: "Approve"
},
style: "primary",
value: "approve",
action_id: "approve_button"
}
}
];
// Variation 2: List formatting
const items = ['Item 1', 'Item 2', 'Item 3'];
const formattedList = items.map((item, i) => `${i + 1}. ${item}`).join('\n');
// Variation 3: Status indicators
function getStatusEmoji(status) {
const statusMap = {
'success': '✅',
'warning': '⚠️',
'error': '❌',
'info': ''
};
return statusMap[status] || '•';
}
// Variation 4: Truncate long messages
function truncate(text, maxLength = 3000) {
if (text.length <= maxLength) return text;
return text.substring(0, maxLength - 3) + '...';
}
```
---
## Pattern 9: Top N Filtering & Ranking
**Use Case**: RAG pipelines, ranking algorithms, result filtering, leaderboards
**When to use:**
- Getting top results by score
- Filtering best/worst performers
- Building leaderboards
- Relevance ranking
**Key Techniques**: Sorting, slicing, null coalescing, score calculations
### Complete Example
```javascript
// Filter and rank by similarity score, return top results
const ragResponse = $input.item.json;
const chunks = ragResponse.chunks || [];
// Sort by similarity (highest first)
const topChunks = chunks
.sort((a, b) => (b.similarity || 0) - (a.similarity || 0))
.slice(0, 6);
return [{
json: {
query: ragResponse.query,
topChunks: topChunks,
count: topChunks.length,
maxSimilarity: topChunks[0]?.similarity || 0,
minSimilarity: topChunks[topChunks.length - 1]?.similarity || 0,
averageSimilarity: topChunks.reduce((sum, chunk) => sum + (chunk.similarity || 0), 0) / topChunks.length
}
}];
```
### Variations
```javascript
// Variation 1: Top N with minimum threshold
const threshold = 0.7;
const topItems = $input.all()
.filter(item => item.json.score >= threshold)
.sort((a, b) => b.json.score - a.json.score)
.slice(0, 10);
// Variation 2: Bottom N (worst performers)
const bottomItems = $input.all()
.sort((a, b) => a.json.score - b.json.score) // Ascending
.slice(0, 5);
// Variation 3: Top N by multiple criteria
const ranked = $input.all()
.map(item => ({
...item,
compositeScore: (item.json.relevance * 0.6) + (item.json.recency * 0.4)
}))
.sort((a, b) => b.compositeScore - a.compositeScore)
.slice(0, 10);
// Variation 4: Percentile filtering
const allScores = $input.all().map(item => item.json.score).sort((a, b) => b - a);
const percentile95 = allScores[Math.floor(allScores.length * 0.05)];
const topPercentile = $input.all().filter(item => item.json.score >= percentile95);
```
---
## Pattern 10: String Aggregation & Reporting
**Use Case**: Report generation, log aggregation, content concatenation, summary creation
**When to use:**
- Combining multiple text outputs
- Generating reports from data
- Aggregating logs or messages
- Creating formatted summaries
**Key Techniques**: Array joining, string concatenation, template literals, timestamp handling
### Complete Example
```javascript
// Aggregate multiple text inputs into formatted report
const allItems = $input.all();
// Collect all messages
const messages = allItems.map(item => item.json.message);
// Build report
const header = `🎯 **Daily Summary Report**\n📅 ${new Date().toLocaleString()}\n📊 Total Items: ${messages.length}\n\n`;
const divider = '\n\n---\n\n';
const footer = `\n\n---\n\n✅ Report generated at ${new Date().toISOString()}`;
const finalReport = header + messages.join(divider) + footer;
return [{
json: {
report: finalReport,
messageCount: messages.length,
generatedAt: new Date().toISOString(),
reportLength: finalReport.length
}
}];
```
### Variations
```javascript
// Variation 1: Numbered list
const numberedReport = allItems
.map((item, index) => `${index + 1}. ${item.json.title}\n ${item.json.description}`)
.join('\n\n');
// Variation 2: Markdown table
const headers = '| Name | Status | Score |\n|------|--------|-------|\n';
const rows = allItems
.map(item => `| ${item.json.name} | ${item.json.status} | ${item.json.score} |`)
.join('\n');
const table = headers + rows;
// Variation 3: HTML report
const htmlReport = `
<!DOCTYPE html>
<html>
<head><title>Report</title></head>
<body>
<h1>Report - ${new Date().toLocaleDateString()}</h1>
<ul>
${allItems.map(item => `<li>${item.json.title}: ${item.json.value}</li>`).join('\n ')}
</ul>
</body>
</html>
`;
// Variation 4: JSON summary
const summary = {
generated: new Date().toISOString(),
totalItems: allItems.length,
items: allItems.map(item => item.json),
statistics: {
total: allItems.reduce((sum, item) => sum + (item.json.value || 0), 0),
average: allItems.reduce((sum, item) => sum + (item.json.value || 0), 0) / allItems.length,
max: Math.max(...allItems.map(item => item.json.value || 0)),
min: Math.min(...allItems.map(item => item.json.value || 0))
}
};
```
---
## Choosing the Right Pattern
### Pattern Selection Guide
| Your Goal | Use Pattern |
|-----------|-------------|
| Combine multiple API responses | Pattern 1 (Multi-source Aggregation) |
| Extract mentions or keywords | Pattern 2 (Regex Filtering) |
| Parse formatted text | Pattern 3 (Markdown Parsing) |
| Detect changes in data | Pattern 4 (JSON Comparison) |
| Prepare form data for CRM | Pattern 5 (CRM Transformation) |
| Process GitHub releases | Pattern 6 (Release Processing) |
| Add computed fields | Pattern 7 (Array Transformation) |
| Format Slack messages | Pattern 8 (Block Kit Formatting) |
| Get top results | Pattern 9 (Top N Filtering) |
| Create text reports | Pattern 10 (String Aggregation) |
### Combining Patterns
Many real workflows combine multiple patterns:
```javascript
// Example: Multi-source aggregation + Top N filtering
const allItems = $input.all();
const aggregated = [];
// Pattern 1: Aggregate from different sources
for (const item of allItems) {
// ... aggregation logic
aggregated.push(normalizedItem);
}
// Pattern 9: Get top 10 by score
const top10 = aggregated
.sort((a, b) => b.score - a.score)
.slice(0, 10);
// Pattern 10: Generate report
const report = `Top 10 Items:\n\n${top10.map((item, i) => `${i + 1}. ${item.title} (${item.score})`).join('\n')}`;
return [{json: {report, items: top10}}];
```
---
## Summary
**Most Useful Patterns**:
1. Multi-source Aggregation - Combining data from APIs, databases
2. Top N Filtering - Rankings, leaderboards, best results
3. Data Transformation - CRM data, field mapping, enrichment
**Key Techniques Across Patterns**:
- Array methods (map, filter, reduce, sort, slice)
- Regex for pattern matching
- Object manipulation and destructuring
- Error handling with optional chaining
- Template literals for formatting
**See Also**:
- [DATA_ACCESS.md](DATA_ACCESS.md) - Data access methods
- [ERROR_PATTERNS.md](ERROR_PATTERNS.md) - Avoid common mistakes
- [BUILTIN_FUNCTIONS.md](BUILTIN_FUNCTIONS.md) - Built-in helpers