Data Transformation with Functions
Functions allow you to transform, enrich, filter and validate your $event data in real-time before it reaches your data warehouse. Write custom JavaScript logic to modify $events, add derived fields, integrate external data, or filter out unwanted traffic.
What are Functions?
Functions are JavaScript code snippets that execute on every $event flowing through your pipeline. They run in a secure, isolated sandbox between $event validation and warehouse delivery.
Data flow with Functions:
Source → Schema Validation → [Functions] → Destination
What You Can Do with Functions
Transform
- Modify field values
- Restructure $event data
- Normalize formats
- Convert data types
Enrich
- Add derived fields (calculations)
- Add geo-location data from IP
- Parse user agents
- Lookup external data (APIs, databases)
Filter
- Remove bot traffic
- Filter test $events
- Sample high-volume $events
- Route $events conditionally
Validate
- Apply custom business logic
- Cross-field validation
- Check data consistency
Function Basics
Function Structure
Every function receives an $event object and must return:
- The modified
$eventto continue processing nullto drop the $event (won't be delivered)
function transform($event) {
// Your transformation logic here
return $event; // Continue processing
// return null; // Drop $event
}
Available JavaScript Features
Functions support ES6+ JavaScript with these additional support for network requests via fetch.
Not supported:
- ❌ File system access
- ❌ Global state/variables between executions
- ❌ setTimeout/setInterval
- ❌ require/import (use built-in utilities)
Common Transformation Patterns
1. Add Derived Fields
Calculate new fields from existing data:
function transform($event) {
// Add tax calculation
if ($event.amount) {
$event.amount_with_tax = $event.amount * 1.08;
$event.tax_amount = $event.amount * 0.08;
}
// Add is_high_value flag
$event.is_high_value = $event.amount > 1000;
// Add computed timestamp fields
const eventDate = new Date($event.timestamp);
$event.event_date = eventDate.toISOString().split('T')[0]; // "2024-01-15"
$event.event_hour = eventDate.getUTCHours();
$event.event_day_of_week = eventDate.getUTCDay(); // 0=Sunday
return $event;
}
2. Data Normalization
Standardize values and formats:
function transform($event) {
// Normalize email to lowercase
if ($event.email) {
$event.email = $event.email.toLowerCase().trim();
}
// Normalize phone numbers (remove formatting)
if ($event.phone) {
$event.phone = $event.phone.replace(/[^0-9]/g, '');
}
// Normalize currency codes to uppercase
if ($event.currency) {
$event.currency = $event.currency.toUpperCase();
}
// Normalize country codes
if ($event.country) {
$event.country = $event.country.toUpperCase(); // US, GB, FR
}
// Round amounts to 2 decimals
if ($event.amount) {
$event.amount = Math.round($event.amount * 100) / 100;
}
return $event;
}
3. Data Enrichment
Add context from existing fields:
function transform($event) {
// Parse user agent for device info
const ua = $event.context.user_agent || '';
$event.device_type = /mobile/i.test(ua) ? 'mobile' :
/tablet/i.test(ua) ? 'tablet' : 'desktop';
$event.is_mobile = /mobile/i.test(ua);
// Extract browser
if (/chrome/i.test(ua)) $event.browser = 'Chrome';
else if (/safari/i.test(ua)) $event.browser = 'Safari';
else if (/firefox/i.test(ua)) $event.browser = 'Firefox';
else $event.browser = 'Other';
// Parse UTM parameters from URL
if ($event.page_url) {
const url = new URL($event.page_url);
$event.utm_source = url.searchParams.get('utm_source');
$event.utm_medium = url.searchParams.get('utm_medium');
$event.utm_campaign = url.searchParams.get('utm_campaign');
}
// Add session context
$event.session_count = ($event.user_properties?.session_count || 0) + 1;
$event.is_first_session = $event.session_count === 1;
return $event;
}
4. Event Filtering
Remove unwanted $events:
function transform($event) {
// Filter test users
if ($event.user_id?.startsWith('test_')) {
return null; // Drop $event
}
// Filter internal IP addresses
const internalIPs = ['192.168.', '10.0.', '172.16.'];
if (internalIPs.some(ip => $event.context.ip?.startsWith(ip))) {
return null;
}
// Filter bot traffic by user agent
const botPatterns = /bot|crawler|spider|scraper/i;
if (botPatterns.test($event.context.user_agent)) {
return null;
}
// Filter incomplete $events
if (!$event.user_id || !$event.timestamp) {
return null;
}
return $event;
}
5. Sampling
Reduce $event volume for high-traffic sources:
function transform($event) {
// Sample 10% of page_view $events
if ($event.event_name === 'page_view') {
if (Math.random() > 0.1) {
return null; // Drop 90% of $events
}
$event.is_sampled = true;
$event.sample_rate = 0.1;
}
// Always keep critical events (100%)
const criticalEvents = ['purchase_completed', 'signup_completed'];
if (criticalEvents.includes($event.event_name)) {
$event.is_sampled = false;
$event.sample_rate = 1.0;
}
return $event;
}
6. Data Masking & Privacy
Anonymize PII for compliance:
function transform($event) {
// Hash email addresses
if ($event.email) {
$event.email_hash = hashEmail($event.email); // Built-in function
delete $event.email; // Remove original
}
// Anonymize IP address (GDPR)
if ($event.context.ip) {
// Remove last octet: 192.168.1.100 → 192.168.1.0
$event.context.ip = $event.context.ip.replace(/\.\d+$/, '.0');
}
// Pseudonymize user ID
if ($event.user_id) {
$event.user_id_hash = hashString($event.user_id);
delete $event.user_id;
}
// Redact sensitive fields
if ($event.credit_card) {
$event.credit_card_last4 = $event.credit_card.slice(-4);
delete $event.credit_card;
}
return $event;
}
Built-in Utility Functions
DataBridge provides built-in utility functions for common operations:
Hashing Functions
function transform($event) {
// Hash email (SHA-256)
$event.email_hash = hashEmail($event.email);
// Output: "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"
// Hash any string
$event.user_id_hash = hashString($event.user_id);
// MD5 hash (faster, less secure)
$event.session_hash = md5($event.session_id);
return $event;
}
Geo-IP Lookup
function transform($event) {
// Lookup geo data from IP address
const geoData = geoIP($event.ip);
$event.country = geoData.country; // "US"
$event.country_name = geoData.country_name; // "United States"
$event.region = geoData.region; // "CA"
$event.city = geoData.city; // "San Francisco"
$event.latitude = geoData.latitude; // 37.7749
$event.longitude = geoData.longitude; // -122.4194
$event.timezone = geoData.timezone; // "America/Los_Angeles"
return $event;
}
User Agent Parsing
function transform($event) {
// Parse user agent string
const ua = parseUserAgent($event.user_agent);
$event.browser = ua.browser; // "Chrome"
$event.browser_version = ua.browser_version; // "120.0"
$event.os = ua.os; // "macOS"
$event.os_version = ua.os_version; // "14.2"
$event.device = ua.device; // "Desktop"
$event.device_brand = ua.device_brand; // "Apple"
$event.device_model = ua.device_model; // "MacBook Pro"
return $event;
}
URL Parsing
function transform($event) {
// Parse URL components
if ($event.page_url) {
const url = parseURL($event.page_url);
$event.url_protocol = url.protocol; // "https:"
$event.url_host = url.host; // "example.com"
$event.url_path = url.pathname; // "/products/123"
$event.url_query = url.search; // "?utm_source=google"
$event.url_hash = url.hash; // "#reviews"
// Extract query parameters
$event.utm_source = url.searchParams.get('utm_source');
$event.utm_medium = url.searchParams.get('utm_medium');
}
return $event;
}
Advanced Patterns
Error Handling
Always handle errors gracefully:
function transform($event) {
try {
// Risky operation
$event.parsed_data = JSON.parse($event.raw_json);
$event.total = $event.parsed_data.items.reduce((sum, item) => sum + item.price, 0);
} catch (error) {
// Log error but don't drop $event
console.error('Transformation error:', error.message);
$event._transform_error = error.message;
$event._transform_failed = true;
// Continue with original $event
}
return $event;
}
Type Conversion
Ensure correct data types:
function transform($event) {
// String to number
if (typeof $event.quantity === 'string') {
$event.quantity = parseInt($event.quantity, 10);
}
// Number to string
if (typeof $event.order_id === 'number') {
$event.order_id = $event.order_id.toString();
}
// Boolean conversion
$event.is_active = $event.status === 'active';
$event.has_discount = parseFloat($event.discount || 0) > 0;
// Array conversion
if (typeof $event.tags === 'string') {
$event.tags = $event.tags.split(',').map(t => t.trim());
}
return $event;
}
Complex Field Mapping
Restructure nested objects:
function transform($event) {
// Flatten nested user object
if ($event.user) {
$event.user_id = $event.user.id;
$event.user_email = $event.user.email;
$event.user_name = $event.user.name;
$event.user_created_at = $event.user.created_at;
delete $event.user; // Remove original nested object
}
// Create nested structure
$event.purchase_info = {
order_id: $event.order_id,
amount: $event.amount,
currency: $event.currency,
items: $event.items,
timestamp: $event.timestamp
};
// Clean up top-level fields
delete $event.order_id;
delete $event.amount;
delete $event.currency;
delete $event.items;
return $event;
}
Lookup Tables
Use constant lookups for enrichment:
function transform($event) {
// Country code to currency mapping
const countryCurrencyMap = {
'US': 'USD',
'GB': 'GBP',
'EU': 'EUR',
'JP': 'JPY',
'CN': 'CNY'
};
if (!$event.currency && $event.country) {
$event.currency = countryCurrencyMap[$event.country] || 'USD';
}
// Product category mapping
const productCategories = {
'PROD-001': 'Electronics',
'PROD-002': 'Clothing',
'PROD-003': 'Books'
};
if ($event.product_id) {
$event.product_category = productCategories[$event.product_id] || 'Other';
}
return $event;
}
Async Functions (External API Calls)
For external data enrichment, use async functions:
async function transform($event) {
// Lookup user profile from API
if ($event.user_id) {
try {
const userProfile = await fetch(`https://api.yourcompany.com/users/${$event.user_id}`);
$event.user_segment = userProfile.segment;
$event.user_lifetime_value = userProfile.ltv;
$event.user_tier = userProfile.tier;
} catch (error) {
console.error('User lookup failed:', error);
$event._enrichment_error = 'user_lookup_failed';
}
}
return $event;
}
⚠️ Important notes on async functions:
- Maximum execution time: 1 second per $event
- Failed API calls won't drop $events
- Use caching to reduce API calls
Testing Functions
1. Use the Function Editor
DataBridge provides a built-in function editor with:
- Syntax highlighting
- Live validation
- Test runner with sample events
2. Test with Sample $events
// Test input
{
"event_name": "purchase_completed",
"user_id": "user_12345",
"amount": 149.99,
"currency": "usd",
"timestamp": "2024-01-15T10:30:00Z"
}
// After transformation
{
"event_name": "purchase_completed",
"user_id": "user_12345",
"amount": 149.99,
"currency": "USD", // normalized
"amount_with_tax": 161.99, // calculated
"is_high_value": false, // derived
"event_date": "2024-01-15", // extracted
"timestamp": "2024-01-15T10:30:00Z"
}
3. Monitor Function Performance
Track function execution in the dashboard:
- Execution time (avg, p50, p95, p99)
- Error rate (% of failed executions)
- Dropped events (filtered via return null)
- Transformation success rate
Best Practices
1. Keep Functions Simple
Good:
function transform($event) {
$event.amount_with_tax = $event.amount * 1.08;
return $event;
}
Bad (too complex):
function transform($event) {
// 200 lines of complex logic
// Multiple nested conditions
// Hard to debug
}
Solution: Split complex logic into multiple functions
2. Handle Null/Undefined Values
function transform($event) {
// Safe: Check existence first
if ($event.amount) {
$event.total = $event.amount * 1.08;
}
// Safe: Use optional chaining
$event.city = $event.user?.address?.city;
// Safe: Provide defaults
$event.quantity = $event.quantity || 1;
return $event;
}
3. Use Meaningful Variable Names
Good:
const taxRate = 0.08;
const amountWithTax = $event.amount * (1 + taxRate);
Bad:
const x = 0.08;
const y = $event.amount * (1 + x);
4. Log Debug Information
function transform($event) {
console.log('Processing $event:', $event.event_name);
console.log('User ID:', $event.user_id);
if (!$event.user_id) {
console.warn('Missing user_id for $event:', $event._databridge.$event_id);
}
return $event;
}
Logs appear in the function execution log dashboard.
5. Document Your Logic
function transform($event) {
// Enriches purchase $events with tax calculations and flags
// - Adds amount_with_tax (8% tax rate)
// - Adds is_high_value flag (>$1000)
// - Normalizes currency to uppercase
// Tax calculation
const TAX_RATE = 0.08;
$event.amount_with_tax = $event.amount * (1 + TAX_RATE);
// High-value flag
const HIGH_VALUE_THRESHOLD = 1000;
$event.is_high_value = $event.amount > HIGH_VALUE_THRESHOLD;
// Normalize currency
$event.currency = ($event.currency || 'USD').toUpperCase();
return $event;
}
Performance Optimization
1. Avoid Heavy Computations
Bad:
// Complex regex on every $event
const complexRegex = /very|complex|regex|with|many|alternatives/i;
$event.matched = complexRegex.test($event.text);
Good:
// Simple, fast checks
$event.matched = $event.text.includes('keyword');
2. Early Returns
Exit early for filtered $events:
function transform($event) {
// Filter first (fast)
if ($event.user_id?.startsWith('test_')) {
return null;
}
// Then do expensive operations
$event.enriched_data = expensiveOperation($event);
return $event;
}
Function Limits
| Limit | Value | Plan |
|---|---|---|
| Max execution time | 200ms | Free/Startup |
| Max execution time | 1s | Pro/Enterprise |
| Max function size | 10KB | All plans |
| Max console.log output | 10KB | All plans |
| Memory per execution | 64MB | All plans |
Next Steps
Now that you understand Functions:
- Create your first function in a pipeline
- Test with sample data in the function editor
- Monitor function performance in the dashboard
- Explore built-in enrichments for common use cases
- Join our community to share transformation patterns