Code Externalization
Store Python/JavaScript code in separate files instead of inline in workflow JSON, making code easier to version control, review, and maintain.
Overview
Instead of storing code inline in workflow JSON:
{
"nodes": [
{
"parameters": {
"pythonCode": "def process(data):\n return data.upper()\n\nresult = process(input)"
}
}
]
}
Store it in separate files:
Workflow JSON:
{
"nodes": [
{
"parameters": {
"pythonCode": "@@n8n-gitops:include scripts/my-workflow/process_data.py"
}
}
]
}
Script file (n8n/scripts/my-workflow/process_data.py):
def process(data):
return data.upper()
result = process(input)
Benefits
✅ Better version control: See actual code diffs, not escaped JSON strings ✅ Code review: Review Python/JavaScript in native format ✅ IDE support: Syntax highlighting, linting, autocomplete ✅ Reusability: Share code between workflows ✅ Testing: Test code independently of workflows
Include Directive Syntax
Basic Syntax
@@n8n-gitops:include <path>
Where <path> is relative to n8n/ directory and must be under scripts/.
Examples
@@n8n-gitops:include scripts/payments/retry.py
@@n8n-gitops:include scripts/utilities/format.js
@@n8n-gitops:include scripts/my-workflow/helper.py
Path Rules
✅ Allowed:
scripts/payments/process.pyscripts/my-workflow/transform.jsscripts/utils/helpers/format.py
❌ Not allowed:
../secrets.txt(path traversal)/etc/passwd(absolute path)config/api-keys.json(not under scripts/)
Exporting with Externalization
Externalization is controlled by the externalize_code flag in n8n/manifests/workflows.yaml (default: true from create-project).
# n8n/manifests/workflows.yaml
externalize_code: true # extract code to n8n/scripts/ and add include directives
Steps:
- Set
externalize_codein the manifest to your desired mode. - Run
n8n-gitops export(no extra flags needed). - Review generated scripts in
n8n/scripts/<workflow-name>/and include directives in workflow JSON.
Supported Code Fields
Code is extracted from these node parameter fields:
| Field | Extension | Example Node Type |
|---|---|---|
pythonCode | .py | Code (Python) |
jsCode | .js | Code (JavaScript) |
code | .js | Function, Function Item |
functionCode | .js | Function (legacy) |
File Naming
Format: {node-name}_{field-name}.{ext}
Examples:
- Node "Process Data" with
pythonCode→Process_Data.py - Node "Transform" with
jsCode→Transform.js - Node "Calculate-Price" with
code→Calculate_Price_code.js
Directory Structure
my-n8n-project/
├── n8n/
│ ├── workflows/
│ │ ├── payment-processing.json
│ │ └── data-sync.json
│ ├── scripts/
│ │ ├── payment-processing/
│ │ │ ├── validate_payment.py
│ │ │ ├── retry_logic.py
│ │ │ └── format_response.js
│ │ └── data-sync/
│ │ └── transform_data.py
│ └── manifests/
│ └── workflows.yaml
Switching Between Modes
From Inline to Externalized
- Set
externalize_code: trueinn8n/manifests/workflows.yaml. - Run
n8n-gitops export.
Result: script files are created in n8n/scripts/ and workflow JSON uses include directives.
From Externalized to Inline
- Set
externalize_code: falseinn8n/manifests/workflows.yaml. - Run
n8n-gitops export.
Result: script files under n8n/scripts/ are removed and workflow JSON contains inline code.
Deployment with Includes
During deployment, include directives are automatically resolved:
n8n-gitops deploy
The deployment process:
- Reads workflow JSON
- Detects
@@n8n-gitops:includedirectives - Loads referenced script files
- Replaces directives with actual code
- Deploys to n8n with inline code
Important: n8n always receives workflows with inline code. Include directives are a Git-only feature.
Example Workflow
1. Export with Externalization
Ensure externalize_code: true is set in n8n/manifests/workflows.yaml, then run:
n8n-gitops export
2. Edit Script File
Edit n8n/scripts/payment-processing/validate_payment.py:
def validate_payment(amount, currency):
"""Validate payment parameters."""
if amount <= 0:
raise ValueError("Amount must be positive")
if currency not in ["USD", "EUR", "GBP"]:
raise ValueError(f"Unsupported currency: {currency}")
return {"valid": True, "amount": amount, "currency": currency}
# Main execution
payment = items[0].json
result = validate_payment(payment["amount"], payment["currency"])
3. Commit Changes
git add n8n/scripts/payment-processing/validate_payment.py
git commit -m "Improve payment validation logic"
git tag v1.1.0
4. Deploy
n8n-gitops deploy --git-ref v1.1.0
The updated code is automatically injected into the workflow during deployment.
Workflow JSON Example
Before Externalization
{
"name": "Payment Processing",
"nodes": [
{
"name": "Validate Payment",
"type": "n8n-nodes-base.code",
"parameters": {
"pythonCode": "def validate(amount):\n if amount <= 0:\n raise ValueError('Invalid')\n return amount\n\nresult = validate(items[0].json.amount)"
}
},
{
"name": "Format Response",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "const format = (data) => {\n return { status: 'ok', data };\n};\n\nreturn format(items[0].json);"
}
}
]
}
After Externalization
Workflow JSON:
{
"name": "Payment Processing",
"nodes": [
{
"name": "Validate Payment",
"type": "n8n-nodes-base.code",
"parameters": {
"pythonCode": "@@n8n-gitops:include scripts/payment-processing/Validate_Payment.py"
}
},
{
"name": "Format Response",
"type": "n8n-nodes-base.code",
"parameters": {
"jsCode": "@@n8n-gitops:include scripts/payment-processing/Format_Response.js"
}
}
]
}
n8n/scripts/payment-processing/Validate_Payment.py:
def validate(amount):
if amount <= 0:
raise ValueError('Invalid')
return amount
result = validate(items[0].json.amount)
n8n/scripts/payment-processing/Format_Response.js:
const format = (data) => {
return { status: 'ok', data };
};
return format(items[0].json);
Best Practices
1. Always Use Externalization in Git
# Set externalize_code: true (default) and export
n8n-gitops export
This gives you the best version control experience.
2. Use Descriptive Node Names
Good:
- "Validate Payment Data"
- "Transform User Input"
- "Format API Response"
Bad:
- "Code"
- "Python"
- "Node 1"
Node names become part of filenames, so make them descriptive.
3. Keep Scripts Directory Clean
Don't manually create files in n8n/scripts/. Let n8n-gitops export generate them based on externalize_code in the manifest.
4. Add Comments
Since code is in separate files, add proper comments:
"""
Validate payment data before processing.
Checks:
- Amount is positive
- Currency is supported
- Required fields are present
"""
def validate_payment(data):
# Implementation...
5. Test Scripts Independently
You can test extracted scripts with standard Python/JavaScript tools:
# Python
python3 n8n/scripts/payment/validate.py
# JavaScript
node n8n/scripts/payment/format.js
Validation
Validate include directives before deployment:
n8n-gitops validate
This checks:
- Include paths are valid
- Referenced files exist
- Paths don't escape
scripts/directory - No path traversal attempts
See Also
- Export - Export workflows with externalization
- Deployment - Deploy workflows with includes
- Manifest File - Workflow manifest format