Initial commit
This commit is contained in:
748
skills/n8n-code-python/SKILL.md
Normal file
748
skills/n8n-code-python/SKILL.md
Normal file
@@ -0,0 +1,748 @@
|
||||
---
|
||||
name: n8n-code-python
|
||||
description: Write Python code in n8n Code nodes. Use when writing Python in n8n, using _input/_json/_node syntax, working with standard library, or need to understand Python limitations in n8n Code nodes.
|
||||
---
|
||||
|
||||
# Python Code Node (Beta)
|
||||
|
||||
Expert guidance for writing Python code in n8n Code nodes.
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Important: JavaScript First
|
||||
|
||||
**Recommendation**: Use **JavaScript for 95% of use cases**. Only use Python when:
|
||||
- You need specific Python standard library functions
|
||||
- You're significantly more comfortable with Python syntax
|
||||
- You're doing data transformations better suited to Python
|
||||
|
||||
**Why JavaScript is preferred:**
|
||||
- Full n8n helper functions ($helpers.httpRequest, etc.)
|
||||
- Luxon DateTime library for advanced date/time operations
|
||||
- No external library limitations
|
||||
- Better n8n documentation and community support
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
# Basic template for Python Code nodes
|
||||
items = _input.all()
|
||||
|
||||
# Process data
|
||||
processed = []
|
||||
for item in items:
|
||||
processed.append({
|
||||
"json": {
|
||||
**item["json"],
|
||||
"processed": True,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
})
|
||||
|
||||
return processed
|
||||
```
|
||||
|
||||
### Essential Rules
|
||||
|
||||
1. **Consider JavaScript first** - Use Python only when necessary
|
||||
2. **Access data**: `_input.all()`, `_input.first()`, or `_input.item`
|
||||
3. **CRITICAL**: Must return `[{"json": {...}}]` format
|
||||
4. **CRITICAL**: Webhook data is under `_json["body"]` (not `_json` directly)
|
||||
5. **CRITICAL LIMITATION**: **No external libraries** (no requests, pandas, numpy)
|
||||
6. **Standard library only**: json, datetime, re, base64, hashlib, urllib.parse, math, random, statistics
|
||||
|
||||
---
|
||||
|
||||
## Mode Selection Guide
|
||||
|
||||
Same as JavaScript - choose based on your use case:
|
||||
|
||||
### Run Once for All Items (Recommended - Default)
|
||||
|
||||
**Use this mode for:** 95% of use cases
|
||||
|
||||
- **How it works**: Code executes **once** regardless of input count
|
||||
- **Data access**: `_input.all()` or `_items` array (Native mode)
|
||||
- **Best for**: Aggregation, filtering, batch processing, transformations
|
||||
- **Performance**: Faster for multiple items (single execution)
|
||||
|
||||
```python
|
||||
# Example: Calculate total from all items
|
||||
all_items = _input.all()
|
||||
total = sum(item["json"].get("amount", 0) for item in all_items)
|
||||
|
||||
return [{
|
||||
"json": {
|
||||
"total": total,
|
||||
"count": len(all_items),
|
||||
"average": total / len(all_items) if all_items else 0
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
### Run Once for Each Item
|
||||
|
||||
**Use this mode for:** Specialized cases only
|
||||
|
||||
- **How it works**: Code executes **separately** for each input item
|
||||
- **Data access**: `_input.item` or `_item` (Native mode)
|
||||
- **Best for**: Item-specific logic, independent operations, per-item validation
|
||||
- **Performance**: Slower for large datasets (multiple executions)
|
||||
|
||||
```python
|
||||
# Example: Add processing timestamp to each item
|
||||
item = _input.item
|
||||
|
||||
return [{
|
||||
"json": {
|
||||
**item["json"],
|
||||
"processed": True,
|
||||
"processed_at": datetime.now().isoformat()
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Python Modes: Beta vs Native
|
||||
|
||||
n8n offers two Python execution modes:
|
||||
|
||||
### Python (Beta) - Recommended
|
||||
- **Use**: `_input`, `_json`, `_node` helper syntax
|
||||
- **Best for**: Most Python use cases
|
||||
- **Helpers available**: `_now`, `_today`, `_jmespath()`
|
||||
- **Import**: `from datetime import datetime`
|
||||
|
||||
```python
|
||||
# Python (Beta) example
|
||||
items = _input.all()
|
||||
now = _now # Built-in datetime object
|
||||
|
||||
return [{
|
||||
"json": {
|
||||
"count": len(items),
|
||||
"timestamp": now.isoformat()
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
### Python (Native) (Beta)
|
||||
- **Use**: `_items`, `_item` variables only
|
||||
- **No helpers**: No `_input`, `_now`, etc.
|
||||
- **More limited**: Standard Python only
|
||||
- **Use when**: Need pure Python without n8n helpers
|
||||
|
||||
```python
|
||||
# Python (Native) example
|
||||
processed = []
|
||||
|
||||
for item in _items:
|
||||
processed.append({
|
||||
"json": {
|
||||
"id": item["json"].get("id"),
|
||||
"processed": True
|
||||
}
|
||||
})
|
||||
|
||||
return processed
|
||||
```
|
||||
|
||||
**Recommendation**: Use **Python (Beta)** for better n8n integration.
|
||||
|
||||
---
|
||||
|
||||
## Data Access Patterns
|
||||
|
||||
### Pattern 1: _input.all() - Most Common
|
||||
|
||||
**Use when**: Processing arrays, batch operations, aggregations
|
||||
|
||||
```python
|
||||
# Get all items from previous node
|
||||
all_items = _input.all()
|
||||
|
||||
# Filter, transform as needed
|
||||
valid = [item for item in all_items if item["json"].get("status") == "active"]
|
||||
|
||||
processed = []
|
||||
for item in valid:
|
||||
processed.append({
|
||||
"json": {
|
||||
"id": item["json"]["id"],
|
||||
"name": item["json"]["name"]
|
||||
}
|
||||
})
|
||||
|
||||
return processed
|
||||
```
|
||||
|
||||
### Pattern 2: _input.first() - Very Common
|
||||
|
||||
**Use when**: Working with single objects, API responses
|
||||
|
||||
```python
|
||||
# Get first item only
|
||||
first_item = _input.first()
|
||||
data = first_item["json"]
|
||||
|
||||
return [{
|
||||
"json": {
|
||||
"result": process_data(data),
|
||||
"processed_at": datetime.now().isoformat()
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
### Pattern 3: _input.item - Each Item Mode Only
|
||||
|
||||
**Use when**: In "Run Once for Each Item" mode
|
||||
|
||||
```python
|
||||
# Current item in loop (Each Item mode only)
|
||||
current_item = _input.item
|
||||
|
||||
return [{
|
||||
"json": {
|
||||
**current_item["json"],
|
||||
"item_processed": True
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
### Pattern 4: _node - Reference Other Nodes
|
||||
|
||||
**Use when**: Need data from specific nodes in workflow
|
||||
|
||||
```python
|
||||
# Get output from specific node
|
||||
webhook_data = _node["Webhook"]["json"]
|
||||
http_data = _node["HTTP Request"]["json"]
|
||||
|
||||
return [{
|
||||
"json": {
|
||||
"combined": {
|
||||
"webhook": webhook_data,
|
||||
"api": http_data
|
||||
}
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
**See**: [DATA_ACCESS.md](DATA_ACCESS.md) for comprehensive guide
|
||||
|
||||
---
|
||||
|
||||
## Critical: Webhook Data Structure
|
||||
|
||||
**MOST COMMON MISTAKE**: Webhook data is nested under `["body"]`
|
||||
|
||||
```python
|
||||
# ❌ WRONG - Will raise KeyError
|
||||
name = _json["name"]
|
||||
email = _json["email"]
|
||||
|
||||
# ✅ CORRECT - Webhook data is under ["body"]
|
||||
name = _json["body"]["name"]
|
||||
email = _json["body"]["email"]
|
||||
|
||||
# ✅ SAFER - Use .get() for safe access
|
||||
webhook_data = _json.get("body", {})
|
||||
name = webhook_data.get("name")
|
||||
```
|
||||
|
||||
**Why**: Webhook node wraps all request data under `body` property. This includes POST data, query parameters, and JSON payloads.
|
||||
|
||||
**See**: [DATA_ACCESS.md](DATA_ACCESS.md) for full webhook structure details
|
||||
|
||||
---
|
||||
|
||||
## Return Format Requirements
|
||||
|
||||
**CRITICAL RULE**: Always return list of dictionaries with `"json"` key
|
||||
|
||||
### Correct Return Formats
|
||||
|
||||
```python
|
||||
# ✅ Single result
|
||||
return [{
|
||||
"json": {
|
||||
"field1": value1,
|
||||
"field2": value2
|
||||
}
|
||||
}]
|
||||
|
||||
# ✅ Multiple results
|
||||
return [
|
||||
{"json": {"id": 1, "data": "first"}},
|
||||
{"json": {"id": 2, "data": "second"}}
|
||||
]
|
||||
|
||||
# ✅ List comprehension
|
||||
transformed = [
|
||||
{"json": {"id": item["json"]["id"], "processed": True}}
|
||||
for item in _input.all()
|
||||
if item["json"].get("valid")
|
||||
]
|
||||
return transformed
|
||||
|
||||
# ✅ Empty result (when no data to return)
|
||||
return []
|
||||
|
||||
# ✅ Conditional return
|
||||
if should_process:
|
||||
return [{"json": processed_data}]
|
||||
else:
|
||||
return []
|
||||
```
|
||||
|
||||
### Incorrect Return Formats
|
||||
|
||||
```python
|
||||
# ❌ WRONG: Dictionary without list wrapper
|
||||
return {
|
||||
"json": {"field": value}
|
||||
}
|
||||
|
||||
# ❌ WRONG: List without json wrapper
|
||||
return [{"field": value}]
|
||||
|
||||
# ❌ WRONG: Plain string
|
||||
return "processed"
|
||||
|
||||
# ❌ WRONG: Incomplete structure
|
||||
return [{"data": value}] # Should be {"json": value}
|
||||
```
|
||||
|
||||
**Why it matters**: Next nodes expect list format. Incorrect format causes workflow execution to fail.
|
||||
|
||||
**See**: [ERROR_PATTERNS.md](ERROR_PATTERNS.md) #2 for detailed error solutions
|
||||
|
||||
---
|
||||
|
||||
## Critical Limitation: No External Libraries
|
||||
|
||||
**MOST IMPORTANT PYTHON LIMITATION**: Cannot import external packages
|
||||
|
||||
### What's NOT Available
|
||||
|
||||
```python
|
||||
# ❌ NOT AVAILABLE - Will raise ModuleNotFoundError
|
||||
import requests # ❌ No
|
||||
import pandas # ❌ No
|
||||
import numpy # ❌ No
|
||||
import scipy # ❌ No
|
||||
from bs4 import BeautifulSoup # ❌ No
|
||||
import lxml # ❌ No
|
||||
```
|
||||
|
||||
### What IS Available (Standard Library)
|
||||
|
||||
```python
|
||||
# ✅ AVAILABLE - Standard library only
|
||||
import json # ✅ JSON parsing
|
||||
import datetime # ✅ Date/time operations
|
||||
import re # ✅ Regular expressions
|
||||
import base64 # ✅ Base64 encoding/decoding
|
||||
import hashlib # ✅ Hashing functions
|
||||
import urllib.parse # ✅ URL parsing
|
||||
import math # ✅ Math functions
|
||||
import random # ✅ Random numbers
|
||||
import statistics # ✅ Statistical functions
|
||||
```
|
||||
|
||||
### Workarounds
|
||||
|
||||
**Need HTTP requests?**
|
||||
- ✅ Use **HTTP Request node** before Code node
|
||||
- ✅ Or switch to **JavaScript** and use `$helpers.httpRequest()`
|
||||
|
||||
**Need data analysis (pandas/numpy)?**
|
||||
- ✅ Use Python **statistics** module for basic stats
|
||||
- ✅ Or switch to **JavaScript** for most operations
|
||||
- ✅ Manual calculations with lists and dictionaries
|
||||
|
||||
**Need web scraping (BeautifulSoup)?**
|
||||
- ✅ Use **HTTP Request node** + **HTML Extract node**
|
||||
- ✅ Or switch to **JavaScript** with regex/string methods
|
||||
|
||||
**See**: [STANDARD_LIBRARY.md](STANDARD_LIBRARY.md) for complete reference
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns Overview
|
||||
|
||||
Based on production workflows, here are the most useful Python patterns:
|
||||
|
||||
### 1. Data Transformation
|
||||
Transform all items with list comprehensions
|
||||
|
||||
```python
|
||||
items = _input.all()
|
||||
|
||||
return [
|
||||
{
|
||||
"json": {
|
||||
"id": item["json"].get("id"),
|
||||
"name": item["json"].get("name", "Unknown").upper(),
|
||||
"processed": True
|
||||
}
|
||||
}
|
||||
for item in items
|
||||
]
|
||||
```
|
||||
|
||||
### 2. Filtering & Aggregation
|
||||
Sum, filter, count with built-in functions
|
||||
|
||||
```python
|
||||
items = _input.all()
|
||||
total = sum(item["json"].get("amount", 0) for item in items)
|
||||
valid_items = [item for item in items if item["json"].get("amount", 0) > 0]
|
||||
|
||||
return [{
|
||||
"json": {
|
||||
"total": total,
|
||||
"count": len(valid_items)
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
### 3. String Processing with Regex
|
||||
Extract patterns from text
|
||||
|
||||
```python
|
||||
import re
|
||||
|
||||
items = _input.all()
|
||||
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
|
||||
|
||||
all_emails = []
|
||||
for item in items:
|
||||
text = item["json"].get("text", "")
|
||||
emails = re.findall(email_pattern, text)
|
||||
all_emails.extend(emails)
|
||||
|
||||
# Remove duplicates
|
||||
unique_emails = list(set(all_emails))
|
||||
|
||||
return [{
|
||||
"json": {
|
||||
"emails": unique_emails,
|
||||
"count": len(unique_emails)
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
### 4. Data Validation
|
||||
Validate and clean data
|
||||
|
||||
```python
|
||||
items = _input.all()
|
||||
validated = []
|
||||
|
||||
for item in items:
|
||||
data = item["json"]
|
||||
errors = []
|
||||
|
||||
# Validate fields
|
||||
if not data.get("email"):
|
||||
errors.append("Email required")
|
||||
if not data.get("name"):
|
||||
errors.append("Name required")
|
||||
|
||||
validated.append({
|
||||
"json": {
|
||||
**data,
|
||||
"valid": len(errors) == 0,
|
||||
"errors": errors if errors else None
|
||||
}
|
||||
})
|
||||
|
||||
return validated
|
||||
```
|
||||
|
||||
### 5. Statistical Analysis
|
||||
Calculate statistics with statistics module
|
||||
|
||||
```python
|
||||
from statistics import mean, median, stdev
|
||||
|
||||
items = _input.all()
|
||||
values = [item["json"].get("value", 0) for item in items if "value" in item["json"]]
|
||||
|
||||
if values:
|
||||
return [{
|
||||
"json": {
|
||||
"mean": mean(values),
|
||||
"median": median(values),
|
||||
"stdev": stdev(values) if len(values) > 1 else 0,
|
||||
"min": min(values),
|
||||
"max": max(values),
|
||||
"count": len(values)
|
||||
}
|
||||
}]
|
||||
else:
|
||||
return [{"json": {"error": "No values found"}}]
|
||||
```
|
||||
|
||||
**See**: [COMMON_PATTERNS.md](COMMON_PATTERNS.md) for 10 detailed Python patterns
|
||||
|
||||
---
|
||||
|
||||
## Error Prevention - Top 5 Mistakes
|
||||
|
||||
### #1: Importing External Libraries (Python-Specific!)
|
||||
|
||||
```python
|
||||
# ❌ WRONG: Trying to import external library
|
||||
import requests # ModuleNotFoundError!
|
||||
|
||||
# ✅ CORRECT: Use HTTP Request node or JavaScript
|
||||
# Add HTTP Request node before Code node
|
||||
# OR switch to JavaScript and use $helpers.httpRequest()
|
||||
```
|
||||
|
||||
### #2: Empty Code or Missing Return
|
||||
|
||||
```python
|
||||
# ❌ WRONG: No return statement
|
||||
items = _input.all()
|
||||
# Processing...
|
||||
# Forgot to return!
|
||||
|
||||
# ✅ CORRECT: Always return data
|
||||
items = _input.all()
|
||||
# Processing...
|
||||
return [{"json": item["json"]} for item in items]
|
||||
```
|
||||
|
||||
### #3: Incorrect Return Format
|
||||
|
||||
```python
|
||||
# ❌ WRONG: Returning dict instead of list
|
||||
return {"json": {"result": "success"}}
|
||||
|
||||
# ✅ CORRECT: List wrapper required
|
||||
return [{"json": {"result": "success"}}]
|
||||
```
|
||||
|
||||
### #4: KeyError on Dictionary Access
|
||||
|
||||
```python
|
||||
# ❌ WRONG: Direct access crashes if missing
|
||||
name = _json["user"]["name"] # KeyError!
|
||||
|
||||
# ✅ CORRECT: Use .get() for safe access
|
||||
name = _json.get("user", {}).get("name", "Unknown")
|
||||
```
|
||||
|
||||
### #5: Webhook Body Nesting
|
||||
|
||||
```python
|
||||
# ❌ WRONG: Direct access to webhook data
|
||||
email = _json["email"] # KeyError!
|
||||
|
||||
# ✅ CORRECT: Webhook data under ["body"]
|
||||
email = _json["body"]["email"]
|
||||
|
||||
# ✅ BETTER: Safe access with .get()
|
||||
email = _json.get("body", {}).get("email", "no-email")
|
||||
```
|
||||
|
||||
**See**: [ERROR_PATTERNS.md](ERROR_PATTERNS.md) for comprehensive error guide
|
||||
|
||||
---
|
||||
|
||||
## Standard Library Reference
|
||||
|
||||
### Most Useful Modules
|
||||
|
||||
```python
|
||||
# JSON operations
|
||||
import json
|
||||
data = json.loads(json_string)
|
||||
json_output = json.dumps({"key": "value"})
|
||||
|
||||
# Date/time
|
||||
from datetime import datetime, timedelta
|
||||
now = datetime.now()
|
||||
tomorrow = now + timedelta(days=1)
|
||||
formatted = now.strftime("%Y-%m-%d")
|
||||
|
||||
# Regular expressions
|
||||
import re
|
||||
matches = re.findall(r'\d+', text)
|
||||
cleaned = re.sub(r'[^\w\s]', '', text)
|
||||
|
||||
# Base64 encoding
|
||||
import base64
|
||||
encoded = base64.b64encode(data).decode()
|
||||
decoded = base64.b64decode(encoded)
|
||||
|
||||
# Hashing
|
||||
import hashlib
|
||||
hash_value = hashlib.sha256(text.encode()).hexdigest()
|
||||
|
||||
# URL parsing
|
||||
import urllib.parse
|
||||
params = urllib.parse.urlencode({"key": "value"})
|
||||
parsed = urllib.parse.urlparse(url)
|
||||
|
||||
# Statistics
|
||||
from statistics import mean, median, stdev
|
||||
average = mean([1, 2, 3, 4, 5])
|
||||
```
|
||||
|
||||
**See**: [STANDARD_LIBRARY.md](STANDARD_LIBRARY.md) for complete reference
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Always Use .get() for Dictionary Access
|
||||
|
||||
```python
|
||||
# ✅ SAFE: Won't crash if field missing
|
||||
value = item["json"].get("field", "default")
|
||||
|
||||
# ❌ RISKY: Crashes if field doesn't exist
|
||||
value = item["json"]["field"]
|
||||
```
|
||||
|
||||
### 2. Handle None/Null Values Explicitly
|
||||
|
||||
```python
|
||||
# ✅ GOOD: Default to 0 if None
|
||||
amount = item["json"].get("amount") or 0
|
||||
|
||||
# ✅ GOOD: Check for None explicitly
|
||||
text = item["json"].get("text")
|
||||
if text is None:
|
||||
text = ""
|
||||
```
|
||||
|
||||
### 3. Use List Comprehensions for Filtering
|
||||
|
||||
```python
|
||||
# ✅ PYTHONIC: List comprehension
|
||||
valid = [item for item in items if item["json"].get("active")]
|
||||
|
||||
# ❌ VERBOSE: Manual loop
|
||||
valid = []
|
||||
for item in items:
|
||||
if item["json"].get("active"):
|
||||
valid.append(item)
|
||||
```
|
||||
|
||||
### 4. Return Consistent Structure
|
||||
|
||||
```python
|
||||
# ✅ CONSISTENT: Always list with "json" key
|
||||
return [{"json": result}] # Single result
|
||||
return results # Multiple results (already formatted)
|
||||
return [] # No results
|
||||
```
|
||||
|
||||
### 5. Debug with print() Statements
|
||||
|
||||
```python
|
||||
# Debug statements appear in browser console (F12)
|
||||
items = _input.all()
|
||||
print(f"Processing {len(items)} items")
|
||||
print(f"First item: {items[0] if items else 'None'}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## When to Use Python vs JavaScript
|
||||
|
||||
### Use Python When:
|
||||
- ✅ You need `statistics` module for statistical operations
|
||||
- ✅ You're significantly more comfortable with Python syntax
|
||||
- ✅ Your logic maps well to list comprehensions
|
||||
- ✅ You need specific standard library functions
|
||||
|
||||
### Use JavaScript When:
|
||||
- ✅ You need HTTP requests ($helpers.httpRequest())
|
||||
- ✅ You need advanced date/time (DateTime/Luxon)
|
||||
- ✅ You want better n8n integration
|
||||
- ✅ **For 95% of use cases** (recommended)
|
||||
|
||||
### Consider Other Nodes When:
|
||||
- ❌ Simple field mapping → Use **Set** node
|
||||
- ❌ Basic filtering → Use **Filter** node
|
||||
- ❌ Simple conditionals → Use **IF** or **Switch** node
|
||||
- ❌ HTTP requests only → Use **HTTP Request** node
|
||||
|
||||
---
|
||||
|
||||
## Integration with Other Skills
|
||||
|
||||
### Works With:
|
||||
|
||||
**n8n Expression Syntax**:
|
||||
- Expressions use `{{ }}` syntax in other nodes
|
||||
- Code nodes use Python directly (no `{{ }}`)
|
||||
- When to use expressions vs code
|
||||
|
||||
**n8n MCP Tools Expert**:
|
||||
- How to find Code node: `search_nodes({query: "code"})`
|
||||
- Get configuration help: `get_node_essentials("nodes-base.code")`
|
||||
- Validate code: `validate_node_operation()`
|
||||
|
||||
**n8n Node Configuration**:
|
||||
- Mode selection (All Items vs Each Item)
|
||||
- Language selection (Python vs JavaScript)
|
||||
- Understanding property dependencies
|
||||
|
||||
**n8n Workflow Patterns**:
|
||||
- Code nodes in transformation step
|
||||
- When to use Python vs JavaScript in patterns
|
||||
|
||||
**n8n Validation Expert**:
|
||||
- Validate Code node configuration
|
||||
- Handle validation errors
|
||||
- Auto-fix common issues
|
||||
|
||||
**n8n Code JavaScript**:
|
||||
- When to use JavaScript instead
|
||||
- Comparison of JavaScript vs Python features
|
||||
- Migration from Python to JavaScript
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Checklist
|
||||
|
||||
Before deploying Python Code nodes, verify:
|
||||
|
||||
- [ ] **Considered JavaScript first** - Using Python only when necessary
|
||||
- [ ] **Code is not empty** - Must have meaningful logic
|
||||
- [ ] **Return statement exists** - Must return list of dictionaries
|
||||
- [ ] **Proper return format** - Each item: `{"json": {...}}`
|
||||
- [ ] **Data access correct** - Using `_input.all()`, `_input.first()`, or `_input.item`
|
||||
- [ ] **No external imports** - Only standard library (json, datetime, re, etc.)
|
||||
- [ ] **Safe dictionary access** - Using `.get()` to avoid KeyError
|
||||
- [ ] **Webhook data** - Access via `["body"]` if from webhook
|
||||
- [ ] **Mode selection** - "All Items" for most cases
|
||||
- [ ] **Output consistent** - All code paths return same structure
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
### Related Files
|
||||
- [DATA_ACCESS.md](DATA_ACCESS.md) - Comprehensive Python data access patterns
|
||||
- [COMMON_PATTERNS.md](COMMON_PATTERNS.md) - 10 Python patterns for n8n
|
||||
- [ERROR_PATTERNS.md](ERROR_PATTERNS.md) - Top 5 errors and solutions
|
||||
- [STANDARD_LIBRARY.md](STANDARD_LIBRARY.md) - Complete standard library reference
|
||||
|
||||
### n8n Documentation
|
||||
- Code Node Guide: https://docs.n8n.io/code/code-node/
|
||||
- Python in n8n: https://docs.n8n.io/code/builtin/python-modules/
|
||||
|
||||
---
|
||||
|
||||
**Ready to write Python in n8n Code nodes - but consider JavaScript first!** Use Python for specific needs, reference the error patterns guide to avoid common mistakes, and leverage the standard library effectively.
|
||||
Reference in New Issue
Block a user