Documentation Index Fetch the complete documentation index at: https://docs.datalyr.com/llms.txt
Use this file to discover all available pages before exploring further.
Advanced SDK features and patterns for identity resolution, performance optimization, and complex tracking scenarios.
What It Covers
Advanced Topics:
Identity resolution and linking
Event batching and queuing
Offline support
Performance optimization
Error handling and retries
Plugin architecture
Custom integrations
Identity Resolution
Understanding Identity
DATALYR uses multiple identifiers to track users across sessions and devices:
Identity Fields:
anonymousId: Persistent anonymous identifier (set by SDK)
visitorId: Browser/device visitor ID (Web/Mobile SDKs)
userId: Your application’s user ID (after login)
distinctId: Resolved ID (userId if available, else anonymousId)
Anonymous Tracking
Web SDK:
// Automatically creates anonymousId on first visit
datalyr . init ({ workspaceId: 'abc123' });
// Get anonymous ID
const anonId = datalyr . getAnonymousId ();
// Returns: "anon_abc123xyz"
Server SDK:
// SDK creates persistent anonymousId
const datalyr = new Datalyr ( 'dk_your_api_key' );
// Track with anonymous ID
await datalyr . track ({
anonymousId: datalyr . getAnonymousId (),
event: 'page_view'
});
Mobile SDKs:
// iOS: Automatically creates anonymousId
let anonId = DatalyrSDK. shared . getAnonymousId ()
// React Native
const anonId = Datalyr. getAnonymousId ();
Linking Anonymous to Known Users
Scenario: User visits site anonymously, then signs up.
Step 1: Anonymous Tracking (Web SDK)
// User visits site - Web SDK tracks anonymously
datalyr . track ( 'product_view' , {
product_id: 'prod_123'
});
// anonymousId: "anon_abc123"
Step 2: User Signs Up
// User creates account
datalyr . identify ( 'user_456' , {
email: '[email protected] ' ,
name: 'John Doe'
});
// Links anonymousId "anon_abc123" to userId "user_456"
Result: All previous anonymous activity now attributed to user_456.
Web to Server:
// Client-side (Web SDK)
const anonymousId = datalyr . getAnonymousId ();
// Send to server on signup
fetch ( '/api/signup' , {
method: 'POST' ,
body: JSON . stringify ({
email: '[email protected] ' ,
anonymousId: anonymousId
})
});
// Server-side (Server SDK)
app . post ( '/api/signup' , async ( req , res ) => {
const userId = await createUser ( req . body . email );
// Link anonymous to known user
await datalyr . track ({
userId: userId ,
anonymousId: req . body . anonymousId ,
event: 'signup' ,
properties: {
email: req . body . email
}
});
});
Mobile to Server:
// Mobile app (React Native)
const anonymousId = Datalyr . getAnonymousId ();
// Send to backend
const response = await fetch ( '/api/purchase' , {
method: 'POST' ,
body: JSON . stringify ({
userId: currentUser . id ,
anonymousId: anonymousId ,
orderId: order . id
})
});
// Server processes webhook
await datalyr . track ({
userId: req . body . userId ,
anonymousId: req . body . anonymousId ,
event: 'purchase' ,
properties: {
order_id: req . body . orderId
}
});
Aliasing
Use Case: User has multiple IDs (email, phone, external ID).
// Web SDK: Connect new ID to existing
datalyr . alias ( 'user_new_id' , 'user_old_id' );
// Server SDK
await datalyr . track ({
event: 'alias' ,
userId: 'user_new_id' ,
anonymousId: 'user_old_id'
});
Result: Both IDs resolve to same canonical user.
Event Batching
How Batching Works
SDKs batch events for efficiency instead of sending individually.
Default Behavior:
Events queued in memory
Batch sent when flushAt size reached
Or when flushInterval elapsed
Automatically flushed on page unload/app background
Configuration
Web SDK:
datalyr . init ({
workspaceId: 'abc123' ,
batchSize: 10 , // Events per batch
flushInterval: 5000 , // Flush every 5 seconds
flushAt: 10 // Auto-flush at 10 events
});
Server SDK:
const datalyr = new Datalyr ({
apiKey: 'dk_your_api_key' ,
flushAt: 20 , // Larger batches for server
flushInterval: 10000
});
Mobile SDKs:
await Datalyr . initialize ({
apiKey: 'dk_your_api_key' ,
batchSize: 10 ,
flushInterval: 30000
});
Manual Flushing
Force Immediate Send:
// Web SDK
await datalyr . flush ();
// Server SDK
await datalyr . flush ();
// Mobile SDKs
await Datalyr . flush ();
Use Cases:
Before page navigation
Before app closes
After critical events
Before serverless function exits
Batch Size Optimization
Small Batches (1-5 events):
Real-time tracking
Low traffic sites
Critical events
Medium Batches (10-20 events):
Default for most sites
Balanced latency/performance
Large Batches (50-100 events):
High traffic applications
Bulk imports
Non-time-sensitive tracking
Offline Support
How Offline Queue Works
SDKs queue events when offline and send when connection restored.
Features:
Automatic network detection
Persistent queue (survives app restart)
Automatic retry with backoff
Max queue size limit
Configuration
Web SDK:
datalyr . init ({
workspaceId: 'abc123' ,
maxOfflineQueueSize: 100 , // Max queued events
maxRetries: 5 , // Retry attempts
retryDelay: 1000 // Initial retry delay (ms)
});
Server SDK:
const datalyr = new Datalyr ({
apiKey: 'dk_your_api_key' ,
maxQueueSize: 1000 ,
retryLimit: 3
});
Mobile SDKs:
await Datalyr . initialize ({
apiKey: 'dk_your_api_key' ,
maxQueueSize: 100 ,
maxRetries: 3
});
Handling Offline State
Check Network Status:
// Web SDK
const status = datalyr . getNetworkStatus ();
console . log ( 'Online:' , status . online );
console . log ( 'Pending events:' , status . queueSize );
// Mobile SDKs
const status = Datalyr . getStatus ();
console . log ( 'Queue size:' , status . queueStats . queueSize );
Queue Overflow:
When queue exceeds maxQueueSize, oldest events dropped first.
Lazy Loading (Web SDK)
Defer Script Loading:
< script defer src = "https://track.datalyr.com/dl.js"
data-workspace-id = "abc123" >
</ script >
Uses defer attribute to load script without blocking page render.
Reduce Event Volume
Track Selectively:
// Bad: Track every mousemove
document . addEventListener ( 'mousemove' , () => {
datalyr . track ( 'mouse_move' ); // Thousands of events
});
// Good: Track meaningful interactions
button . addEventListener ( 'click' , () => {
datalyr . track ( 'button_click' ); // Few events
});
Debounce Frequent Events
import { debounce } from 'lodash' ;
const trackScroll = debounce (() => {
datalyr . track ( 'scroll' , {
depth: window . scrollY
});
}, 1000 );
window . addEventListener ( 'scroll' , trackScroll );
Use Super Properties
Instead of Repeating Properties:
// Bad
datalyr . track ( 'page_view' , { version: '2.0' , env: 'prod' });
datalyr . track ( 'button_click' , { version: '2.0' , env: 'prod' });
datalyr . track ( 'form_submit' , { version: '2.0' , env: 'prod' });
// Good
datalyr . setSuperProperties ({
version: '2.0' ,
env: 'prod'
});
datalyr . track ( 'page_view' );
datalyr . track ( 'button_click' );
datalyr . track ( 'form_submit' );
Increase Batch Size
// For high-volume tracking
datalyr . init ({
workspaceId: 'abc123' ,
flushAt: 50 , // Larger batches
flushInterval: 30000 // Less frequent flushes
});
Error Handling
Automatic Retries
All SDKs retry failed requests automatically.
Retry Logic:
Exponential backoff (1s, 2s, 4s, 8s…)
Max retry attempts configurable
Client errors (4xx) not retried
Server errors (5xx) retried
Configuration:
datalyr . init ({
workspaceId: 'abc123' ,
maxRetries: 5 ,
retryDelay: 1000
});
Error Handling
Web SDK:
try {
await datalyr . track ( 'event_name' );
} catch ( error ) {
console . error ( 'Tracking failed:' , error );
}
// View recent errors
const errors = datalyr . getErrors ();
Server SDK:
try {
await datalyr . track ({
userId: 'user_123' ,
event: 'purchase'
});
} catch ( error ) {
console . error ( 'Tracking failed:' , error . message );
// Handle error (log, alert, etc.)
}
Mobile SDKs:
do {
try await DatalyrSDK. shared . track ( "event_name" )
} catch {
print ( "Tracking error: \( error ) " )
}
Graceful Degradation
SDKs designed to fail silently without breaking your application.
// Even if SDK fails, app continues
datalyr . track ( 'button_click' );
handleButtonClick (); // Always executes
Plugin Architecture (Web SDK)
Custom Plugins
Create Plugin:
const myPlugin = {
name: 'MyPlugin' ,
initialize : ( datalyr ) => {
console . log ( 'Plugin initialized' );
},
track : ( eventName , properties ) => {
console . log ( 'Event tracked:' , eventName );
},
identify : ( userId , traits ) => {
console . log ( 'User identified:' , userId );
},
page : ( properties ) => {
console . log ( 'Page viewed:' , properties . url );
}
};
// Use plugin
datalyr . init ({
workspaceId: 'abc123' ,
plugins: [ myPlugin ]
});
Plugin Use Cases
Google Analytics Integration:
const googleAnalyticsPlugin = {
name: 'GoogleAnalytics' ,
initialize : ( datalyr ) => {
// Initialize GA
gtag ( 'config' , 'GA_MEASUREMENT_ID' );
},
track : ( eventName , properties ) => {
// Send to GA
gtag ( 'event' , eventName , properties );
}
};
Console Logger:
const consoleLoggerPlugin = {
name: 'ConsoleLogger' ,
track : ( eventName , properties ) => {
console . log ( `[Event] ${ eventName } ` , properties );
},
identify : ( userId , traits ) => {
console . log ( `[Identify] ${ userId } ` , traits );
}
};
Custom Destination:
const customDestinationPlugin = {
name: 'CustomDestination' ,
track : async ( eventName , properties ) => {
await fetch ( 'https://my-api.com/track' , {
method: 'POST' ,
body: JSON . stringify ({ eventName , properties })
});
}
};
Advanced Patterns
Conditional Tracking
// Only track in production
if ( process . env . NODE_ENV === 'production' ) {
datalyr . track ( 'event_name' );
}
// Track for specific users
if ( user . plan === 'enterprise' ) {
datalyr . track ( 'feature_usage' );
}
Event Validation
function trackValidated ( eventName , properties ) {
// Validate event name
if ( ! / ^ [ a-z_ ] + $ / . test ( eventName )) {
console . error ( 'Invalid event name:' , eventName );
return ;
}
// Validate properties
if ( properties && typeof properties !== 'object' ) {
console . error ( 'Properties must be object' );
return ;
}
datalyr . track ( eventName , properties );
}
Rate Limiting
import { throttle } from 'lodash' ;
const trackThrottled = throttle (( eventName , properties ) => {
datalyr . track ( eventName , properties );
}, 1000 );
// Max 1 event per second
trackThrottled ( 'rapid_event' );
Context Enrichment
// Add context to all events
datalyr . setSuperProperties ({
app_version: '2.1.0' ,
environment: 'production' ,
user_role: currentUser . role ,
experiment_variant: getExperimentVariant ()
});
Testing SDKs
Debug Mode
Enable Debug Logs:
datalyr . init ({
workspaceId: 'abc123' ,
debug: true
});
Console Output:
[Datalyr] SDK initialized
[Datalyr] Event tracked: button_click
[Datalyr] Flushing 5 events
[Datalyr] Events sent successfully
Testing Environment
Use Test Workspace:
const workspaceId = process . env . NODE_ENV === 'production'
? 'prod_workspace_id'
: 'test_workspace_id' ;
datalyr . init ({ workspaceId });
Mock SDK for Tests
// __mocks__/@datalyr/web.js
export default {
init: jest . fn () ,
track: jest . fn () ,
identify: jest . fn () ,
flush: jest . fn ()
} ;
// test.js
import datalyr from '@datalyr/web' ;
test ( 'tracks button click' , () => {
handleClick ();
expect ( datalyr . track ). toHaveBeenCalledWith ( 'button_click' );
});
Best Practices
Initialize Once:
Create single SDK instance and reuse throughout application.
Flush Before Exit:
Call flush() before page navigation, app close, or function termination.
Use Super Properties:
Set common properties once instead of repeating in every event.
Batch Appropriately:
Balance real-time needs with performance (larger batches = better performance).
Handle Errors:
Wrap tracking in try/catch for critical paths.
Respect Privacy:
Enable Do Not Track, respect user consent, anonymize sensitive data.
Test Thoroughly:
Use debug mode and test workspace during development.
Monitor Queue Size:
Check queue size in high-traffic scenarios to prevent memory issues.
Next Steps
Web SDK Browser tracking guide
Server SDK Backend tracking guide
Mobile SDKs iOS & React Native guide
Event Stream Verify tracked events