Learn how to use static data in n8n to store and reuse constants, configurations, and predefined values across workflows using Set, JSON, environment variables, and more.
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Static data in n8n allows you to store and reuse predefined values across workflows without fetching them from external sources. This is useful for constants, configuration settings, or any data that doesn't change frequently. You can leverage static data through n8n's Set node, JSON node, or by creating dedicated workflows that serve as data repositories.
Step 1: Understanding Static Data in n8n
Static data in n8n refers to fixed information that doesn't change during workflow execution. There are several approaches to use static data in your workflows:
Common methods for working with static data:
Step 2: Using the Set Node for Static Data
The Set node is one of the simplest ways to define static data in n8n.
Here's how to set up a Set node with static data:
// Example of static data structure in Set node
{
"apiKey": "YOUR_API_KEY\_HERE",
"baseUrl": "https://api.example.com",
"defaultParams": {
"format": "json",
"limit": 100
},
"userConfig": {
"language": "en",
"timezone": "UTC"
}
}
Step 3: Creating JSON Data Objects
For more complex static data structures, you can use the JSON node:
Example JSON node configuration:
{
"products": [
{
"id": 1,
"name": "Product A",
"price": 29.99,
"categories": ["electronics", "accessories"]
},
{
"id": 2,
"name": "Product B",
"price": 49.99,
"categories": ["electronics", "gadgets"]
}
],
"taxRates": {
"standard": 0.20,
"reduced": 0.10,
"zero": 0.00
},
"shippingOptions": [
{"name": "Standard", "price": 5.99, "days": "3-5"},
{"name": "Express", "price": 12.99, "days": "1-2"}
]
}
Step 4: Using Environment Variables
For sensitive or configuration data, environment variables are ideal:
To access environment variables in n8n:
// In a Function node or Expression field
const apiKey = $env.API\_KEY;
const databaseUrl = $env.DATABASE\_URL;
// Example usage in a Function node
return {
json: {
config: {
apiKey: $env.API\_KEY,
endpoint: $env.API\_ENDPOINT,
timeout: parseInt($env.TIMEOUT) || 30000
}
}
}
Step 5: Creating a Data Repository Workflow
For more complex scenarios, you can create a dedicated workflow that serves as a data repository:
Example of a data repository workflow:
Step 6: Using the Function Node for Static Data
The Function node allows you to create and return static data programmatically:
// Function node code for static data
return {
json: {
companyInfo: {
name: "ACME Corporation",
founded: 1985,
headquarters: "New York, NY",
employees: 1250
},
contactDetails: {
phone: "+1-555-123-4567",
email: "[email protected]",
address: "123 Business Ave, Suite 100"
},
departments: [
{id: "sales", head: "Jane Smith", employees: 45},
{id: "engineering", head: "John Doe", employees: 78},
{id: "marketing", head: "Bob Johnson", employees: 34}
]
}
}
Step 7: Using Code Snippets for Data Generation
For more dynamic static data (like dates, IDs, etc.), you can use code:
// Generate static data with some dynamic elements
return {
json: {
// Static elements
apiVersion: "1.2.0",
supportedFormats: ["json", "xml", "csv"],
// Semi-dynamic elements
generatedOn: new Date().toISOString(),
fiscalYear: new Date().getFullYear(),
// Programmatically generated array
monthlyQuotas: Array.from({length: 12}, (\_, i) => ({
month: i + 1,
quota: 10000 + (i \* 500),
bonusThreshold: 15000 + (i \* 750)
}))
}
}
Step 8: Accessing Static Data in Other Nodes
After defining static data, you need to know how to access it:
{{ $node["Set"].json.apiKey }}
{{ $node["JSON"].json.products[0].name }}
{{ $env.DATABASE\_URL }}
Example of accessing nested static data in an HTTP Request node:
// URL field of an HTTP Request node
{{ $node["Set"].json.baseUrl }}/api/{{ $node["Set"].json.version }}/products
// Headers
{
"Authorization": "Bearer {{ $node["Set"].json.apiKey }}",
"Content-Type": "application/json"
}
// Query Parameters
{
"limit": {{ $node["Set"].json.defaultParams.limit }},
"format": "{{ $node["Set"].json.defaultParams.format }}"
}
Step 9: Combining Static and Dynamic Data
Often, you'll need to combine static data with dynamic inputs:
// Function node example
const staticConfig = {
taxRates: {
US: 0.07,
UK: 0.20,
DE: 0.19
},
shippingRates: {
domestic: 5.99,
international: 15.99
}
};
// Get dynamic data from input
const items = $input.json.items || [];
const country = $input.json.country || 'US';
// Calculate totals using static and dynamic data
const subtotal = items.reduce((sum, item) => sum + (item.price \* item.quantity), 0);
const taxRate = staticConfig.taxRates[country] || 0;
const taxAmount = subtotal \* taxRate;
const shippingCost = country === 'US' ? staticConfig.shippingRates.domestic : staticConfig.shippingRates.international;
const total = subtotal + taxAmount + shippingCost;
// Return combined data
return {
json: {
orderSummary: {
subtotal,
taxRate,
taxAmount,
shippingCost,
total
},
processingDetails: {
timestamp: new Date().toISOString(),
countryCode: country,
itemCount: items.length
}
}
}
Step 10: Managing and Updating Static Data
Static data may need updates occasionally. Here are strategies for managing updates:
Consider creating a version control system for your static data:
// Static data with versioning
return {
json: {
\_meta: {
version: "2.0.3",
lastUpdated: "2023-08-15T14:30:00Z",
updatedBy: "admin"
},
configuration: {
// Your actual configuration here
apiEndpoints: {
production: "https://api.example.com/v2",
staging: "https://staging-api.example.com/v2",
development: "https://dev-api.example.com/v2"
},
// Other configuration...
}
}
}
Step 11: Using Static Data for Conditional Logic
Static data is useful for making workflow decisions:
// Function node with conditional logic based on static data
const featureFlags = {
enableNewUI: true,
enableBetaFeatures: false,
logLevel: "warning",
maxRetries: 3,
allowedCountries: ["US", "CA", "UK", "AU", "DE", "FR"]
};
// Get user data from input
const userData = $input.json;
const userCountry = userData.country;
// Apply business logic based on static configuration
if (!featureFlags.allowedCountries.includes(userCountry)) {
return {
json: {
status: "error",
message: "Service not available in your country",
code: "COUNTRY\_RESTRICTED"
}
};
}
// Check if user can access beta features
const canAccessBeta = featureFlags.enableBetaFeatures || userData.userType === "premium";
return {
json: {
status: "success",
uiVersion: featureFlags.enableNewUI ? "new" : "classic",
betaAccess: canAccessBeta,
maxRetries: featureFlags.maxRetries,
// Other processed data...
}
}
Step 12: Creating Reusable Data Templates
For complex applications, create reusable data templates:
// Function node with reusable templates
const templates = {
emailNotification: {
subject: "New notification from {{company}}",
body: "Hello {{name}},\n\nYou have received a new {{type}} notification.\n\nRegards,\nThe {{company}} Team",
footer: "© {{year}} {{company}}. All rights reserved."
},
apiResponse: {
success: {
status: "success",
code: 200,
data: null
},
error: {
status: "error",
code: 400,
message: "An error occurred",
details: null
}
}
};
// Function to fill template variables
function fillTemplate(template, variables) {
let result = JSON.parse(JSON.stringify(template)); // Deep clone
const replaceInString = (str, vars) => {
return str.replace(/{{([^}]+)}}/g, (match, key) => {
return vars[key.trim()] !== undefined ? vars[key.trim()] : match;
});
};
// Recursive function to replace in all string properties
const processObject = (obj, vars) => {
for (const key in obj) {
if (typeof obj[key] === 'string') {
obj[key] = replaceInString(obj[key], vars);
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
processObject(obj[key], vars);
}
}
return obj;
};
return processObject(result, variables);
}
// Use the template with specific data
const companyInfo = {
company: "ACME Inc",
year: new Date().getFullYear()
};
const userData = $input.json;
// Create a customized email notification
const emailData = fillTemplate(templates.emailNotification, {
...companyInfo,
name: userData.fullName,
type: "account update"
});
// Create a customized API response
const apiResponse = fillTemplate(templates.apiResponse.success, {});
apiResponse.data = userData.results;
return {
json: {
email: emailData,
response: apiResponse
}
}
Step 13: Advanced Data Organization with Namespaces
For large applications, organize static data with namespaces:
// Organized static data with namespaces
return {
json: {
system: {
version: "3.2.1",
environment: "production",
maintenanceMode: false,
limits: {
requestsPerMinute: 60,
maxUploadSize: 10485760
}
},
ui: {
theme: "light",
dateFormat: "YYYY-MM-DD",
timeFormat: "24h",
decimalSeparator: ".",
thousandsSeparator: ","
},
pricing: {
currency: "USD",
plans: {
basic: {price: 9.99, features: ["feature1", "feature2"]},
pro: {price: 19.99, features: ["feature1", "feature2", "feature3"]},
enterprise: {price: 49.99, features: ["feature1", "feature2", "feature3", "feature4"]}
},
discounts: {
annual: 0.15,
educational: 0.50,
nonprofit: 0.30
}
},
locations: {
headquarters: {
address: "123 Main St",
city: "New York",
country: "USA"
},
datacenters: [
{region: "us-east", location: "Virginia", status: "active"},
{region: "eu-west", location: "Ireland", status: "active"},
{region: "ap-south", location: "Singapore", status: "maintenance"}
]
}
}
}
Step 14: Making Static Data More Readable
For better maintainability, format complex static data:
// Well-structured static data example
return {
json: {
// API Configuration
api: {
// Base URLs for different environments
baseUrls: {
development: "https://dev-api.example.com/v1",
staging: "https://staging-api.example.com/v1",
production: "https://api.example.com/v1"
},
// Default request options
defaults: {
timeout: 30000,
retries: 3,
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
},
// Rate limiting settings
rateLimits: {
requestsPerMinute: 60,
burstRequests: 10,
cooldownPeriod: 60000
}
},
// Business rules
businessRules: {
// Order processing rules
orders: {
minimumOrderValue: 10.00,
freeShippingThreshold: 50.00,
// Tax rates by region
taxRates: {
domestic: 0.07,
international: {
EU: 0.21,
UK: 0.20,
CA: 0.13,
// Other regions...
}
},
// Shipping costs
shipping: {
domestic: {
standard: 4.99,
express: 12.99
},
international: {
standard: 14.99,
express: 24.99
}
}
},
// User account rules
accounts: {
passwordMinLength: 8,
passwordRequirements: ["uppercase", "lowercase", "number", "special"],
sessionTimeout: 3600,
maxLoginAttempts: 5
}
},
// Content and localization
content: {
// Company information
company: {
name: "ACME Corporation",
slogan: "Building the Future Today",
founded: 1985,
logo: {
light: "https://assets.example.com/logo-light.png",
dark: "https://assets.example.com/logo-dark.png"
}
},
// Error messages
errors: {
general: "An unexpected error occurred. Please try again later.",
notFound: "The requested resource was not found.",
accessDenied: "You don't have permission to access this resource.",
validation: "Please check your input and try again."
}
}
}
}
Step 15: Practical Example - E-commerce Configuration
Let's put it all together with a practical e-commerce example:
// E-commerce static configuration
const storeConfig = {
// Store information
store: {
name: "TechGadgets",
domain: "techgadgets.example",
supportEmail: "[email protected]",
phoneNumber: "+1-555-123-4567",
socialMedia: {
facebook: "https://facebook.com/techgadgets",
twitter: "https://twitter.com/techgadgets",
instagram: "https://instagram.com/techgadgets"
}
},
// Product categories with display settings
categories: [
{id: "smartphones", name: "Smartphones", featured: true, icon: "phone"},
{id: "laptops", name: "Laptops & Computers", featured: true, icon: "laptop"},
{id: "accessories", name: "Accessories", featured: false, icon: "headphones"},
{id: "wearables", name: "Wearable Tech", featured: true, icon: "watch"},
{id: "cameras", name: "Cameras", featured: false, icon: "camera"},
{id: "audio", name: "Audio", featured: false, icon: "speaker"}
],
// Payment options
paymentMethods: [
{id: "credit", name: "Credit Card", enabled: true, fee: 0},
{id: "paypal", name: "PayPal", enabled: true, fee: 0},
{id: "bank", name: "Bank Transfer", enabled: true, fee: 0},
{id: "crypto", name: "Cryptocurrency", enabled: false, fee: 0.01}
],
// Shipping options
shipping: {
methods: [
{id: "standard", name: "Standard Shipping", days: "3-5", base: 4.99},
{id: "express", name: "Express Shipping", days: "1-2", base: 12.99},
{id: "overnight", name: "Overnight", days: "Next day", base: 24.99}
],
freeThreshold: 75,
weightMultiplier: 0.5, // Additional cost per kg
internationalMultiplier: 2.5 // Multiplier for international shipping
},
// Tax configuration
taxes: {
default: 0.07, // 7% default tax rate
byRegion: {
"US-CA": 0.0725, // California
"US-NY": 0.0845, // New York
"US-TX": 0.0625, // Texas
"CA": 0.13, // Canada
"EU": 0.21, // European Union
"UK": 0.20, // United Kingdom
"AU": 0.10 // Australia
},
exemptCategories: ["books", "food", "medicine"]
},
// Discount rules
discounts: {
volumeDiscounts: [
{threshold: 3, discount: 0.05}, // 5% off for 3+ items
{threshold: 5, discount: 0.10}, // 10% off for 5+ items
{threshold: 10, discount: 0.15} // 15% off for 10+ items
],
promoCode: {
"WELCOME10": {discount: 0.10, expiry: "2023-12-31", oneTime: true},
"SUMMER2023": {discount: 0.15, expiry: "2023-08-31", oneTime: false},
"LOYALTY5": {discount: 0.05, expiry: null, oneTime: false}
},
stackable: false // Whether multiple discounts can be combined
},
// Email templates
emailTemplates: {
orderConfirmation: {
subject: "Your TechGadgets Order #{{orderNumber}}",
template: "order-confirmation"
},
shipping: {
subject: "Your TechGadgets Order #{{orderNumber}} Has Shipped",
template: "order-shipped"
},
abandoned: {
subject: "Complete Your TechGadgets Purchase",
template: "cart-abandoned"
}
},
// Feature flags
features: {
reviews: true,
wishlist: true,
comparison: true,
recentlyViewed: true,
recommendations: true,
giftWrapping: false,
subscriptions: false
}
};
// Process order with the static configuration
function processOrder(order) {
// Get shipping cost
let shippingMethod = storeConfig.shipping.methods.find(m => m.id === order.shippingMethodId);
let shippingCost = shippingMethod ? shippingMethod.base : storeConfig.shipping.methods[0].base;
// Add weight-based cost
if (order.weight > 1) {
shippingCost += (order.weight - 1) \* storeConfig.shipping.weightMultiplier;
}
// Apply international multiplier if needed
if (order.country !== 'US') {
shippingCost \*= storeConfig.shipping.internationalMultiplier;
}
// Apply free shipping if order value exceeds threshold
if (order.subtotal >= storeConfig.shipping.freeThreshold) {
shippingCost = 0;
}
// Calculate tax
let taxRate = storeConfig.taxes.default;
const regionKey = order.country === 'US' ? `US-${order.state}` : order.country;
if (storeConfig.taxes.byRegion[regionKey]) {
taxRate = storeConfig.taxes.byRegion[regionKey];
}
const taxAmount = order.subtotal \* taxRate;
// Calculate discounts
let discountAmount = 0;
// Check promo code
if (order.promoCode && storeConfig.discounts.promoCode[order.promoCode]) {
const promo = storeConfig.discounts.promoCode[order.promoCode];
const now = new Date();
// Check if promo is valid
if (!promo.expiry || new Date(promo.expiry) > now) {
discountAmount = order.subtotal \* promo.discount;
}
}
// Check volume discounts
if (order.itemCount >= 3) {
const volumeDiscount = storeConfig.discounts.volumeDiscounts
.filter(vd => vd.threshold <= order.itemCount)
.sort((a, b) => b.threshold - a.threshold)[0];
if (volumeDiscount) {
// Only apply if better than promo code or if stackable
const potentialVolumeDiscount = order.subtotal \* volumeDiscount.discount;
if (storeConfig.discounts.stackable) {
discountAmount += potentialVolumeDiscount;
} else if (potentialVolumeDiscount > discountAmount) {
discountAmount = potentialVolumeDiscount;
}
}
}
// Calculate total
const total = order.subtotal + shippingCost + taxAmount - discountAmount;
return {
orderSummary: {
subtotal: order.subtotal,
shipping: shippingCost,
tax: taxAmount,
discount: discountAmount,
total: total
},
shippingDetails: {
method: shippingMethod.name,
estimatedDelivery: shippingMethod.days,
trackingAvailable: order.shippingMethodId !== 'standard'
},
paymentDetails: {
method: storeConfig.paymentMethods.find(m => m.id === order.paymentMethodId).name
},
storeInfo: {
name: storeConfig.store.name,
supportEmail: storeConfig.store.supportEmail,
supportPhone: storeConfig.store.phoneNumber
}
};
}
// Example usage with an incoming order
const incomingOrder = $input.json;
return {
json: processOrder(incomingOrder)
}
Conclusion
Static data in n8n provides a powerful way to manage configuration settings, business rules, and constants across your workflows. By using the techniques outlined in this guide—from simple Set nodes to complex data templates and namespaces—you can create more maintainable, configurable, and robust automation workflows. Remember to consider how your static data will be maintained and updated over time, and choose the appropriate approach based on your specific needs.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.