mirror of
https://github.com/czlonkowski/n8n-skills.git
synced 2026-03-17 07:53:08 +00:00
Implements comprehensive Python Code node guidance with critical focus on "NO external libraries" limitation. ## Skill #7 - n8n Code Python **Critical Message**: Use JavaScript for 95% of use cases. Python in n8n has NO external libraries (no requests, pandas, numpy). ### Files Created **Core Skill Files (6 files, 4,205 lines total)**: 1. **SKILL.md** (748 lines) - When to use Python vs JavaScript (95% JavaScript recommendation) - Critical limitation: NO external libraries - Mode selection (All Items vs Each Item) - Data access overview (_input, _json, _node) - Return format requirements - Standard library overview 2. **DATA_ACCESS.md** (702 lines) - _input.all() - Process all items - _input.first() - Get first item - _input.item - Current item (Each Item mode only) - _node["Name"] - Reference other nodes - Webhook body structure (data under ["body"]) - Pattern selection guide - Python vs JavaScript comparison 3. **STANDARD_LIBRARY.md** (974 lines) - Complete reference for available modules - json - JSON parsing and generation - datetime - Date/time operations - re - Regular expressions - base64 - Encoding/decoding - hashlib - Hashing (MD5, SHA256) - urllib.parse - URL operations - math, random, statistics - What's NOT available (requests, pandas, numpy, etc.) - Workarounds for missing libraries 4. **COMMON_PATTERNS.md** (794 lines) - 10 production-tested Python patterns - Multi-source data aggregation - Regex-based filtering - Markdown to structured data - JSON object comparison - CRM data transformation - Release notes processing - Array transformation - Dictionary lookup - Top N filtering - String aggregation - Python vs JavaScript pattern comparison 5. **ERROR_PATTERNS.md** (601 lines) - Top 5 Python-specific errors with solutions - Error #1: ModuleNotFoundError (THE critical Python error) - Error #2: Empty code / missing return - Error #3: KeyError (use .get() instead) - Error #4: IndexError (check bounds first) - Error #5: Incorrect return format - Error prevention checklist - Quick fix reference table - Testing patterns 6. **README.md** (386 lines) - Skill metadata and activation triggers - "JavaScript First" recommendation prominent - What this skill teaches - File structure overview - Integration with other skills - Success metrics checklist - Quick reference guide - Common use cases - Limitations and workarounds - Best practices **Evaluations (5 scenarios)**: 1. **eval-001-module-import-error.json** - Tests understanding of external library limitation - Scenario: ModuleNotFoundError with requests - Expected: Recommend JavaScript or HTTP Request node 2. **eval-002-dictionary-keyerror.json** - Tests safe dictionary access with .get() - Scenario: KeyError when accessing missing field - Expected: Use .get() with default values 3. **eval-003-webhook-body-gotcha.json** - Tests webhook data under ["body"] understanding - Scenario: KeyError when accessing webhook data directly - Expected: Access via data.get("body", {}) 4. **eval-004-return-format-error.json** - Tests proper return format requirement - Scenario: Returning plain dict instead of array - Expected: Return [{"json": {...}}] 5. **eval-005-standard-library-usage.json** - Tests knowledge of available modules - Scenario: What modules for JSON, hashing, dates, regex - Expected: json, hashlib, datetime, re (standard library only) ### Key Features **Critical Limitations Emphasized**: - NO external libraries (no requests, pandas, numpy) - JavaScript recommended for 95% of use cases - Only standard library available - ModuleNotFoundError is #1 Python error **Python-Specific Syntax**: - Underscore prefix: _input, _json, _node (vs $ in JavaScript) - Dictionary access: _json["body"]["field"] (vs dot notation) - Safe access: .get() method with defaults **Complete Standard Library Coverage**: - 15+ modules documented with examples - json, datetime, re, base64, hashlib, urllib.parse - math, random, statistics, collections - Clear list of what's NOT available - Workarounds for missing functionality **Production Patterns**: - 10 tested patterns adapted from JavaScript - Python-specific implementations - List comprehensions and dictionary operations - Standard library usage examples **Error Prevention Focus**: - Top 5 errors cover majority of failures - ModuleNotFoundError prominently featured - Safe dictionary access (.get()) - Proper return format emphasized - Error prevention checklist ### Integration Works seamlessly with: - **n8n Code JavaScript**: Compare approaches, know when to use which - **n8n Expression Syntax**: Different from {{}} expressions - **n8n MCP Tools Expert**: Validate Code node configurations - **n8n Workflow Patterns**: Code nodes in larger workflows - **n8n Node Configuration**: Configure mode and connections ### Statistics - **6 skill files**: 4,205 lines total - **5 evaluations**: Cover critical Python scenarios - **10 patterns**: Production-tested Python code - **15+ modules**: Standard library coverage - **5 top errors**: Prevention and solutions ### Design Principles 1. **JavaScript First**: 95% recommendation throughout 2. **Critical Limitation**: NO external libraries emphasized everywhere 3. **Safe Patterns**: .get() for dicts, bounds checking for lists 4. **Proper Format**: [{"json": {...}}] return format 5. **Standard Library**: Complete reference with examples 6. **Error Prevention**: Top 5 errors with solutions ### Recommendation **Use JavaScript Code node for 95% of use cases.** Use Python only when: - Complex Python-specific logic required - Python standard library features needed - Team more comfortable with Python than JavaScript For HTTP requests, date operations, and most transformations → Use JavaScript. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
703 lines
15 KiB
Markdown
703 lines
15 KiB
Markdown
# Data Access Patterns - Python Code Node
|
|
|
|
Complete guide to accessing data in n8n Code nodes using Python.
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
In n8n Python Code nodes, you access data using **underscore-prefixed** variables: `_input`, `_json`, `_node`.
|
|
|
|
**Data Access Priority** (by common usage):
|
|
1. **`_input.all()`** - Most common - Batch operations, aggregations
|
|
2. **`_input.first()`** - Very common - Single item operations
|
|
3. **`_input.item`** - Common - Each Item mode only
|
|
4. **`_node["NodeName"]["json"]`** - Specific node references
|
|
5. **`_json`** - Direct current item (use `_input` instead)
|
|
|
|
**Python vs JavaScript**:
|
|
| JavaScript | Python (Beta) | Python (Native) |
|
|
|------------|---------------|-----------------|
|
|
| `$input.all()` | `_input.all()` | `_items` |
|
|
| `$input.first()` | `_input.first()` | `_items[0]` |
|
|
| `$input.item` | `_input.item` | `_item` |
|
|
| `$json` | `_json` | `_item["json"]` |
|
|
| `$node["Name"]` | `_node["Name"]` | Not available |
|
|
|
|
---
|
|
|
|
## Pattern 1: _input.all() - Process All Items
|
|
|
|
**Usage**: Most common pattern for batch processing
|
|
|
|
**When to use:**
|
|
- Processing multiple records
|
|
- Aggregating data (sum, count, average)
|
|
- Filtering lists
|
|
- Transforming datasets
|
|
|
|
### Basic Usage
|
|
|
|
```python
|
|
# Get all items from previous node
|
|
all_items = _input.all()
|
|
|
|
# all_items is a list of dictionaries like:
|
|
# [
|
|
# {"json": {"id": 1, "name": "Alice"}},
|
|
# {"json": {"id": 2, "name": "Bob"}}
|
|
# ]
|
|
|
|
print(f"Received {len(all_items)} items")
|
|
|
|
return all_items
|
|
```
|
|
|
|
### Example 1: Filter Active Items
|
|
|
|
```python
|
|
all_items = _input.all()
|
|
|
|
# Filter only active items
|
|
active_items = [
|
|
item for item in all_items
|
|
if item["json"].get("status") == "active"
|
|
]
|
|
|
|
return active_items
|
|
```
|
|
|
|
### Example 2: Transform All Items
|
|
|
|
```python
|
|
all_items = _input.all()
|
|
|
|
# Transform to new structure
|
|
transformed = []
|
|
for item in all_items:
|
|
transformed.append({
|
|
"json": {
|
|
"id": item["json"].get("id"),
|
|
"full_name": f"{item['json'].get('first_name', '')} {item['json'].get('last_name', '')}",
|
|
"email": item["json"].get("email"),
|
|
"processed_at": datetime.now().isoformat()
|
|
}
|
|
})
|
|
|
|
return transformed
|
|
```
|
|
|
|
### Example 3: Aggregate Data
|
|
|
|
```python
|
|
all_items = _input.all()
|
|
|
|
# Calculate total
|
|
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
|
|
}
|
|
}]
|
|
```
|
|
|
|
### Example 4: Sort and Limit
|
|
|
|
```python
|
|
all_items = _input.all()
|
|
|
|
# Get top 5 by score
|
|
sorted_items = sorted(
|
|
all_items,
|
|
key=lambda item: item["json"].get("score", 0),
|
|
reverse=True
|
|
)
|
|
top_five = sorted_items[:5]
|
|
|
|
return [{"json": item["json"]} for item in top_five]
|
|
```
|
|
|
|
### Example 5: Group By Category
|
|
|
|
```python
|
|
all_items = _input.all()
|
|
|
|
# Group items by category
|
|
grouped = {}
|
|
for item in all_items:
|
|
category = item["json"].get("category", "Uncategorized")
|
|
|
|
if category not in grouped:
|
|
grouped[category] = []
|
|
|
|
grouped[category].append(item["json"])
|
|
|
|
# Convert to list format
|
|
return [
|
|
{
|
|
"json": {
|
|
"category": category,
|
|
"items": items,
|
|
"count": len(items)
|
|
}
|
|
}
|
|
for category, items in grouped.items()
|
|
]
|
|
```
|
|
|
|
### Example 6: Deduplicate by ID
|
|
|
|
```python
|
|
all_items = _input.all()
|
|
|
|
# Remove duplicates by ID
|
|
seen = set()
|
|
unique = []
|
|
|
|
for item in all_items:
|
|
item_id = item["json"].get("id")
|
|
|
|
if item_id and item_id not in seen:
|
|
seen.add(item_id)
|
|
unique.append(item)
|
|
|
|
return unique
|
|
```
|
|
|
|
---
|
|
|
|
## Pattern 2: _input.first() - Get First Item
|
|
|
|
**Usage**: Very common for single-item operations
|
|
|
|
**When to use:**
|
|
- Previous node returns single object
|
|
- Working with API responses
|
|
- Getting initial/first data point
|
|
|
|
### Basic Usage
|
|
|
|
```python
|
|
# Get first item from previous node
|
|
first_item = _input.first()
|
|
|
|
# Access the JSON data
|
|
data = first_item["json"]
|
|
|
|
print(f"First item: {data}")
|
|
|
|
return [{"json": data}]
|
|
```
|
|
|
|
### Example 1: Process Single API Response
|
|
|
|
```python
|
|
# Get API response (typically single object)
|
|
response = _input.first()["json"]
|
|
|
|
# Extract what you need
|
|
return [{
|
|
"json": {
|
|
"user_id": response.get("data", {}).get("user", {}).get("id"),
|
|
"user_name": response.get("data", {}).get("user", {}).get("name"),
|
|
"status": response.get("status"),
|
|
"fetched_at": datetime.now().isoformat()
|
|
}
|
|
}]
|
|
```
|
|
|
|
### Example 2: Transform Single Object
|
|
|
|
```python
|
|
data = _input.first()["json"]
|
|
|
|
# Transform structure
|
|
return [{
|
|
"json": {
|
|
"id": data.get("id"),
|
|
"contact": {
|
|
"email": data.get("email"),
|
|
"phone": data.get("phone")
|
|
},
|
|
"address": {
|
|
"street": data.get("street"),
|
|
"city": data.get("city"),
|
|
"zip": data.get("zip")
|
|
}
|
|
}
|
|
}]
|
|
```
|
|
|
|
### Example 3: Validate Single Item
|
|
|
|
```python
|
|
item = _input.first()["json"]
|
|
|
|
# Validation logic
|
|
is_valid = bool(item.get("email") and "@" in item.get("email", ""))
|
|
|
|
return [{
|
|
"json": {
|
|
**item,
|
|
"valid": is_valid,
|
|
"validated_at": datetime.now().isoformat()
|
|
}
|
|
}]
|
|
```
|
|
|
|
### Example 4: Extract Nested Data
|
|
|
|
```python
|
|
response = _input.first()["json"]
|
|
|
|
# Navigate nested structure
|
|
users = response.get("data", {}).get("users", [])
|
|
|
|
return [
|
|
{
|
|
"json": {
|
|
"id": user.get("id"),
|
|
"name": user.get("profile", {}).get("name", "Unknown"),
|
|
"email": user.get("contact", {}).get("email", "no-email")
|
|
}
|
|
}
|
|
for user in users
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
## Pattern 3: _input.item - Current Item (Each Item Mode)
|
|
|
|
**Usage**: Common in "Run Once for Each Item" mode
|
|
|
|
**When to use:**
|
|
- Mode is set to "Run Once for Each Item"
|
|
- Need to process items independently
|
|
- Per-item API calls or validations
|
|
|
|
**IMPORTANT**: Only use in "Each Item" mode. Will be undefined in "All Items" mode.
|
|
|
|
### Basic Usage
|
|
|
|
```python
|
|
# In "Run Once for Each Item" mode
|
|
current_item = _input.item
|
|
data = current_item["json"]
|
|
|
|
print(f"Processing item: {data.get('id')}")
|
|
|
|
return [{
|
|
"json": {
|
|
**data,
|
|
"processed": True
|
|
}
|
|
}]
|
|
```
|
|
|
|
### Example 1: Add Processing Metadata
|
|
|
|
```python
|
|
item = _input.item
|
|
|
|
return [{
|
|
"json": {
|
|
**item["json"],
|
|
"processed": True,
|
|
"processed_at": datetime.now().isoformat(),
|
|
"processing_duration": random.random() * 1000 # Simulated
|
|
}
|
|
}]
|
|
```
|
|
|
|
### Example 2: Per-Item Validation
|
|
|
|
```python
|
|
item = _input.item
|
|
data = item["json"]
|
|
|
|
# Validate this specific item
|
|
errors = []
|
|
|
|
if not data.get("email"):
|
|
errors.append("Email required")
|
|
if not data.get("name"):
|
|
errors.append("Name required")
|
|
if data.get("age") and data["age"] < 18:
|
|
errors.append("Must be 18+")
|
|
|
|
return [{
|
|
"json": {
|
|
**data,
|
|
"valid": len(errors) == 0,
|
|
"errors": errors if errors else None
|
|
}
|
|
}]
|
|
```
|
|
|
|
### Example 3: Conditional Processing
|
|
|
|
```python
|
|
item = _input.item
|
|
data = item["json"]
|
|
|
|
# Process based on item type
|
|
if data.get("type") == "premium":
|
|
return [{
|
|
"json": {
|
|
**data,
|
|
"discount": 0.20,
|
|
"tier": "premium"
|
|
}
|
|
}]
|
|
else:
|
|
return [{
|
|
"json": {
|
|
**data,
|
|
"discount": 0.05,
|
|
"tier": "standard"
|
|
}
|
|
}]
|
|
```
|
|
|
|
---
|
|
|
|
## Pattern 4: _node - Reference Other Nodes
|
|
|
|
**Usage**: Less common, but powerful for specific scenarios
|
|
|
|
**When to use:**
|
|
- Need data from specific named node
|
|
- Combining data from multiple nodes
|
|
|
|
### Basic Usage
|
|
|
|
```python
|
|
# Get output from specific node
|
|
webhook_data = _node["Webhook"]["json"]
|
|
api_data = _node["HTTP Request"]["json"]
|
|
|
|
return [{
|
|
"json": {
|
|
"from_webhook": webhook_data,
|
|
"from_api": api_data
|
|
}
|
|
}]
|
|
```
|
|
|
|
### Example 1: Combine Multiple Sources
|
|
|
|
```python
|
|
# Reference multiple nodes
|
|
webhook = _node["Webhook"]["json"]
|
|
database = _node["Postgres"]["json"]
|
|
api = _node["HTTP Request"]["json"]
|
|
|
|
return [{
|
|
"json": {
|
|
"combined": {
|
|
"webhook": webhook.get("body", {}),
|
|
"db_records": len(database) if isinstance(database, list) else 1,
|
|
"api_response": api.get("status")
|
|
},
|
|
"processed_at": datetime.now().isoformat()
|
|
}
|
|
}]
|
|
```
|
|
|
|
### Example 2: Compare Across Nodes
|
|
|
|
```python
|
|
old_data = _node["Get Old Data"]["json"]
|
|
new_data = _node["Get New Data"]["json"]
|
|
|
|
# Simple comparison
|
|
changes = {
|
|
"added": [n for n in new_data if n.get("id") not in [o.get("id") for o in old_data]],
|
|
"removed": [o for o in old_data if o.get("id") not in [n.get("id") for n in new_data]]
|
|
}
|
|
|
|
return [{
|
|
"json": {
|
|
"changes": changes,
|
|
"summary": {
|
|
"added": len(changes["added"]),
|
|
"removed": len(changes["removed"])
|
|
}
|
|
}
|
|
}]
|
|
```
|
|
|
|
---
|
|
|
|
## Critical: Webhook Data Structure
|
|
|
|
**MOST COMMON MISTAKE**: Forgetting webhook data is nested under `["body"]`
|
|
|
|
### The Problem
|
|
|
|
Webhook node wraps all incoming data under a `"body"` property.
|
|
|
|
### Structure
|
|
|
|
```python
|
|
# Webhook node output structure:
|
|
{
|
|
"headers": {
|
|
"content-type": "application/json",
|
|
"user-agent": "..."
|
|
},
|
|
"params": {},
|
|
"query": {},
|
|
"body": {
|
|
# ← YOUR DATA IS HERE
|
|
"name": "Alice",
|
|
"email": "alice@example.com",
|
|
"message": "Hello!"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Wrong vs Right
|
|
|
|
```python
|
|
# ❌ WRONG: Trying to access directly
|
|
name = _json["name"] # KeyError!
|
|
email = _json["email"] # KeyError!
|
|
|
|
# ✅ CORRECT: Access via ["body"]
|
|
name = _json["body"]["name"] # "Alice"
|
|
email = _json["body"]["email"] # "alice@example.com"
|
|
|
|
# ✅ SAFER: Use .get() for safe access
|
|
webhook_data = _json.get("body", {})
|
|
name = webhook_data.get("name") # None if missing
|
|
email = webhook_data.get("email", "no-email") # Default value
|
|
```
|
|
|
|
### Example: Full Webhook Processing
|
|
|
|
```python
|
|
# Get webhook data from previous node
|
|
webhook_output = _input.first()["json"]
|
|
|
|
# Access the actual payload
|
|
payload = webhook_output.get("body", {})
|
|
|
|
# Access headers if needed
|
|
content_type = webhook_output.get("headers", {}).get("content-type")
|
|
|
|
# Access query parameters if needed
|
|
api_key = webhook_output.get("query", {}).get("api_key")
|
|
|
|
# Process the actual data
|
|
return [{
|
|
"json": {
|
|
# Data from webhook body
|
|
"user_name": payload.get("name"),
|
|
"user_email": payload.get("email"),
|
|
"message": payload.get("message"),
|
|
|
|
# Metadata
|
|
"received_at": datetime.now().isoformat(),
|
|
"content_type": content_type,
|
|
"authenticated": bool(api_key)
|
|
}
|
|
}]
|
|
```
|
|
|
|
### POST Data, Query Params, and Headers
|
|
|
|
```python
|
|
webhook = _input.first()["json"]
|
|
|
|
return [{
|
|
"json": {
|
|
# POST body data
|
|
"form_data": webhook.get("body", {}),
|
|
|
|
# Query parameters (?key=value)
|
|
"query_params": webhook.get("query", {}),
|
|
|
|
# HTTP headers
|
|
"user_agent": webhook.get("headers", {}).get("user-agent"),
|
|
"content_type": webhook.get("headers", {}).get("content-type"),
|
|
|
|
# Request metadata
|
|
"method": webhook.get("method"), # POST, GET, etc.
|
|
"url": webhook.get("url")
|
|
}
|
|
}]
|
|
```
|
|
|
|
---
|
|
|
|
## Choosing the Right Pattern
|
|
|
|
### Decision Tree
|
|
|
|
```
|
|
Do you need ALL items from previous node?
|
|
├─ YES → Use _input.all()
|
|
│
|
|
└─ NO → Do you need just the FIRST item?
|
|
├─ YES → Use _input.first()
|
|
│
|
|
└─ NO → Are you in "Each Item" mode?
|
|
├─ YES → Use _input.item
|
|
│
|
|
└─ NO → Do you need specific node data?
|
|
├─ YES → Use _node["NodeName"]
|
|
└─ NO → Use _input.first() (default)
|
|
```
|
|
|
|
### Quick Reference Table
|
|
|
|
| Scenario | Use This | Example |
|
|
|----------|----------|---------|
|
|
| Sum all amounts | `_input.all()` | `sum(i["json"].get("amount", 0) for i in items)` |
|
|
| Get API response | `_input.first()` | `_input.first()["json"].get("data")` |
|
|
| Process each independently | `_input.item` | `_input.item["json"]` (Each Item mode) |
|
|
| Combine two nodes | `_node["Name"]` | `_node["API"]["json"]` |
|
|
| Filter list | `_input.all()` | `[i for i in items if i["json"].get("active")]` |
|
|
| Transform single object | `_input.first()` | `{**_input.first()["json"], "new": True}` |
|
|
| Webhook data | `_input.first()` | `_input.first()["json"]["body"]` |
|
|
|
|
---
|
|
|
|
## Common Mistakes
|
|
|
|
### Mistake 1: Using _json Without Context
|
|
|
|
```python
|
|
# ❌ RISKY: _json is ambiguous
|
|
value = _json["field"]
|
|
|
|
# ✅ CLEAR: Be explicit
|
|
value = _input.first()["json"]["field"]
|
|
```
|
|
|
|
### Mistake 2: Forgetting ["json"] Property
|
|
|
|
```python
|
|
# ❌ WRONG: Trying to access fields on item dictionary
|
|
items = _input.all()
|
|
names = [item["name"] for item in items] # KeyError!
|
|
|
|
# ✅ CORRECT: Access via ["json"]
|
|
names = [item["json"]["name"] for item in items]
|
|
```
|
|
|
|
### Mistake 3: Using _input.item in All Items Mode
|
|
|
|
```python
|
|
# ❌ WRONG: _input.item is None in "All Items" mode
|
|
data = _input.item["json"] # AttributeError!
|
|
|
|
# ✅ CORRECT: Use appropriate method
|
|
data = _input.first()["json"] # Or _input.all()
|
|
```
|
|
|
|
### Mistake 4: Not Handling Empty Lists
|
|
|
|
```python
|
|
# ❌ WRONG: Crashes if no items
|
|
first = _input.all()[0]["json"] # IndexError!
|
|
|
|
# ✅ CORRECT: Check length first
|
|
items = _input.all()
|
|
if items:
|
|
first = items[0]["json"]
|
|
else:
|
|
return []
|
|
|
|
# ✅ ALSO CORRECT: Use _input.first()
|
|
first = _input.first()["json"] # Built-in safety
|
|
```
|
|
|
|
### Mistake 5: Direct Dictionary Access (KeyError)
|
|
|
|
```python
|
|
# ❌ RISKY: Crashes if key missing
|
|
value = item["json"]["field"] # KeyError!
|
|
|
|
# ✅ SAFE: Use .get()
|
|
value = item["json"].get("field", "default")
|
|
```
|
|
|
|
---
|
|
|
|
## Advanced Patterns
|
|
|
|
### Pattern: Safe Nested Access
|
|
|
|
```python
|
|
# Deep nested access with .get()
|
|
value = (
|
|
_input.first()["json"]
|
|
.get("level1", {})
|
|
.get("level2", {})
|
|
.get("level3", "default")
|
|
)
|
|
```
|
|
|
|
### Pattern: List Comprehension with Filtering
|
|
|
|
```python
|
|
items = _input.all()
|
|
|
|
# Filter and transform in one step
|
|
result = [
|
|
{
|
|
"json": {
|
|
"id": item["json"]["id"],
|
|
"name": item["json"]["name"].upper()
|
|
}
|
|
}
|
|
for item in items
|
|
if item["json"].get("active") and item["json"].get("verified")
|
|
]
|
|
|
|
return result
|
|
```
|
|
|
|
### Pattern: Dictionary Comprehension
|
|
|
|
```python
|
|
items = _input.all()
|
|
|
|
# Create lookup dictionary
|
|
lookup = {
|
|
item["json"]["id"]: item["json"]
|
|
for item in items
|
|
if "id" in item["json"]
|
|
}
|
|
|
|
return [{"json": lookup}]
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
**Most Common Patterns**:
|
|
1. `_input.all()` - Process multiple items, batch operations
|
|
2. `_input.first()` - Single item, API responses
|
|
3. `_input.item` - Each Item mode processing
|
|
|
|
**Critical Rule**:
|
|
- Webhook data is under `["body"]` property
|
|
|
|
**Best Practice**:
|
|
- Use `.get()` for dictionary access to avoid KeyError
|
|
- Always check for empty lists
|
|
- Be explicit: Use `_input.first()["json"]["field"]` instead of `_json["field"]`
|
|
|
|
**See Also**:
|
|
- [SKILL.md](SKILL.md) - Overview and quick start
|
|
- [COMMON_PATTERNS.md](COMMON_PATTERNS.md) - Python-specific patterns
|
|
- [ERROR_PATTERNS.md](ERROR_PATTERNS.md) - Avoid common mistakes
|