When Dify's built-in nodes aren't enough, Code nodes let you write Python or JavaScript logic directly in your workflow. Here's how.
When the Visual Builder Isn't Enough
Dify's drag-and-drop workflow builder covers 80% of use cases. The other 20% -- complex data transformation, calling APIs with custom auth logic, regex parsing, conditional branching based on computed values -- needs a Code node.
Code nodes let you write Python or JavaScript directly inside a Dify workflow. They're one of the most powerful and least documented features in Dify. This guide covers everything you need to use them confidently.
What Code Nodes Can Do
- Transform or reformat data between nodes (parse JSON, manipulate strings, compute values)
- Call external APIs with custom headers, OAuth tokens, or request signing
- Apply conditional logic that's too complex for the built-in IF node
- Parse and clean LLM outputs (extract structured data, strip unwanted formatting)
- Compute scores, rankings, or aggregations across multiple inputs
Code nodes run in a sandboxed environment. They cannot access the filesystem, spawn subprocesses, import arbitrary packages (only a curated set of standard libraries are available), or make network calls unless the node is specifically set up as an HTTP tool.Adding a Code Node
- In the workflow canvas, click the + button between two existing nodes.
- Select Code from the node type list.
- Choose Python 3 or JavaScript as the language.
- Write your function in the editor. The function must return a dict (Python) or object (JS).
The function signature
Your code must define a main() function that receives all input variables and returns an output dict. The return keys become available as variables for downstream nodes.
# Python Code node -- basic structure
def main(input_var_1: str, input_var_2: int) -> dict:
# Process inputs
result = input_var_1.upper()
count = input_var_2 * 2
# Return a dict -- keys become output variables
return {
"processed_text": result,
"doubled_count": count,
}// JavaScript Code node -- same structure
async function main({ input_var_1, input_var_2 }) {
const result = input_var_1.toUpperCase();
const count = input_var_2 * 2;
return {
processed_text: result,
doubled_count: count,
};
}Connecting Inputs and Outputs
In the Code node panel, you define which upstream variables the node receives and what keys it outputs. This is the part most tutorials skip, which causes the most frustration.
Setting up inputs
- In the Code node's Input Variables panel, click Add Variable.
- Give the variable a name (this is the parameter name in your function).
- Set the Type (String, Number, Array, Object).
- Map it to an upstream node's output by clicking the value field and selecting from the variable picker.
Setting up outputs
- In the Output Variables panel, add each key your function returns.
- Set the type for each output key.
- Downstream nodes can now reference these outputs via the variable picker.
If your function returns a key that isn't declared in Output Variables, Dify silently drops it. Always declare every key you return, or downstream nodes will have missing variables.Practical Examples
Example 1: Parse an LLM output into structured fields
LLMs often return semi-structured text even when you ask for JSON. A Code node can clean this up before downstream nodes consume the data.
import json, re
def main(llm_output: str) -> dict:
# Try direct JSON parse first
try:
data = json.loads(llm_output)
return {"name": data.get("name", ""), "score": data.get("score", 0)}
except json.JSONDecodeError:
pass
# Fallback: extract with regex
name_match = re.search(r'Name:\s*(.+)', llm_output)
score_match = re.search(r'Score:\s*(\d+)', llm_output)
return {
"name": name_match.group(1).strip() if name_match else "",
"score": int(score_match.group(1)) if score_match else 0,
}Example 2: Call an external API with a custom auth header
Use the HTTP Request tool node for most API calls, but when you need computed headers (e.g. HMAC signatures, time-based tokens), a Code node is the right place.
import hashlib, hmac, time
def main(api_key: str, api_secret: str, payload: str) -> dict:
# Compute HMAC signature (e.g. for Stripe-style webhook verification)
timestamp = str(int(time.time()))
message = f"{timestamp}.{payload}"
sig = hmac.new(
api_secret.encode(), message.encode(), hashlib.sha256
).hexdigest()
return {
"auth_header": f"t={timestamp},v1={sig}",
"timestamp": timestamp,
}Example 3: Transform a list of items for a downstream loop
def main(raw_items: list) -> dict:
# Clean, deduplicate, and format a list for downstream processing
seen = set()
cleaned = []
for item in raw_items:
key = item.get("id", "").strip().lower()
if key and key not in seen:
seen.add(key)
cleaned.append({
"id": key,
"label": item.get("name", "").title(),
"value": float(item.get("value", 0)),
})
return {"items": cleaned, "count": len(cleaned)}Debugging Code Nodes
- Use the Run This Node button in the node panel to test with sample inputs before wiring the node into your workflow
- Print statements do NOT produce visible output -- use return values to surface debug data
- If your node silently fails, check that all Input Variables are correctly mapped to upstream outputs
- Type mismatches (e.g. passing a string where the function expects a list) cause silent failures -- double-check types in the Input Variables panel
- The execution log (accessible via the workflow's run history) shows the Code node's input and output values for each run -- use this to trace unexpected behaviour
Limitations to Know
- Available Python packages: json, re, math, datetime, hashlib, hmac, base64, urllib -- no pip install
- No filesystem access
- No async in Python nodes (JavaScript nodes support async/await)
- Maximum execution time: 10 seconds -- long-running operations will timeout
- Code nodes cannot call other Dify workflows or agents -- use an HTTP Request node for that