Pixel Pipeline
The pixel pipeline is the core data collection system. It handles all client-side events from merchant storefronts.
Overview
"Pixels" are small JavaScript events sent from the storefront to Cloudflare Workers. Each pixel type represents a specific signal or action.
Storefront JS → POST /api/pixels → pixel-router.js → [specific handler] → D1
Pixel Types
| Type | Description | Handler |
|---|---|---|
session_init | New visitor/session | session-init-handler.js |
basic_security | Content protection triggered | basic-security-handler.js |
bot_detection | Bot signal detected | bot-detection-handler.js |
spy_detection | Spy tool found | spy-detection-handler.js |
ip_blocking | IP/country blocked | ip-blocking-pixel-handler.js |
behavior_analytics | Mouse/scroll patterns | behavior-analytics-handler.js |
checkout_session | Checkout page reached | checkout-session-handler.js |
test_pixel | Validation/testing | test-pixel-handler.js |
Pixel Router
All pixels arrive at a single endpoint and are routed by type:
// pixel-router.js
export async function routePixel(request, env) {
const payload = await request.json();
const { type, shop, ...data } = payload;
switch (type) {
case 'session_init':
return handleSessionInit(shop, data, env);
case 'bot_detection':
return handleBotDetection(shop, data, env);
case 'basic_security':
return handleBasicSecurity(shop, data, env);
// ... other handlers
default:
return new Response('Unknown pixel type', { status: 400 });
}
}
Common Pixel Payload
Every pixel includes these base fields:
interface BasePixel {
type: string; // Pixel type identifier
shop: string; // Shopify shop domain
sessionId: string; // Unique session ID
visitorId: string; // Fingerprint-based visitor ID
timestamp: number; // Client timestamp
page: string; // Current page URL
userAgent: string; // Browser user agent
}
Session Init Handler
The most important pixel - creates visitor identity and session tracking.
// session-init-handler.js
export async function handleSessionInit(shop, data, env) {
const { visitorId, sessionId, fingerprint, deviceInfo } = data;
// 1. Upsert visitor identity
await env.DB.prepare(`
INSERT INTO VisitorIdentity (id, shop, fingerprint, first_seen, last_seen, visit_count)
VALUES (?, ?, ?, ?, ?, 1)
ON CONFLICT (id) DO UPDATE SET
last_seen = excluded.last_seen,
visit_count = visit_count + 1
`).bind(visitorId, shop, fingerprint, Date.now(), Date.now()).run();
// 2. Create session snapshot
await env.DB.prepare(`
INSERT INTO SessionSnapshot (id, shop, visitor_id, started_at, device_info)
VALUES (?, ?, ?, ?, ?)
`).bind(sessionId, shop, visitorId, Date.now(), JSON.stringify(deviceInfo)).run();
// 3. Track daily unique visitors (for billing)
await env.DB.prepare(`
INSERT OR IGNORE INTO DailyUniqueVisitors (shop, date, visitor_id)
VALUES (?, ?, ?)
`).bind(shop, getToday(), visitorId).run();
// 4. Update daily metrics
await incrementMetric(env.DB, shop, 'sessions');
return new Response('OK');
}
Bot Detection Handler
Processes bot-related signals:
// bot-detection-handler.js
export async function handleBotDetection(shop, data, env) {
const {
visitorId,
sessionId,
signalType, // 'selenium' | 'headless' | 'honeypot' | 'trap_link'
confidence,
details
} = data;
// 1. Store raw signal
await env.DB.prepare(`
INSERT INTO BotSignal (id, shop, visitor_id, session_id, signal_type, confidence, details, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`).bind(
generateId(), shop, visitorId, sessionId,
signalType, confidence, JSON.stringify(details), Date.now()
).run();
// 2. Update aggregates
await incrementMetric(env.DB, shop, 'bot_events');
await updateTopIPs(env.DB, shop, data.ip);
await updateTopPages(env.DB, shop, data.page);
return new Response('OK');
}
Aggregation Pattern
Every handler follows the same aggregation pattern:
async function incrementMetric(db, shop, metric) {
const today = new Date().toISOString().split('T')[0];
await db.prepare(`
INSERT INTO DailyMetrics (shop, date, ${metric})
VALUES (?, ?, 1)
ON CONFLICT (shop, date)
DO UPDATE SET ${metric} = ${metric} + 1
`).bind(shop, today).run();
}
async function updateTopIPs(db, shop, ip) {
const today = new Date().toISOString().split('T')[0];
await db.prepare(`
INSERT INTO TopIPsDaily (shop, date, ip, count)
VALUES (?, ?, ?, 1)
ON CONFLICT (shop, date, ip)
DO UPDATE SET count = count + 1
`).bind(shop, today, ip).run();
}
Response Handling
All pixel handlers return quickly to not block the client:
// Fast response pattern
export async function handler(request, env, ctx) {
const data = await request.json();
// Use waitUntil for non-blocking writes
ctx.waitUntil(processPixel(data, env));
// Return immediately
return new Response('OK', { status: 200 });
}
Error Handling
Pixels should never fail the client request:
export async function handlePixel(request, env, ctx) {
try {
const data = await request.json();
ctx.waitUntil(processPixel(data, env));
return new Response('OK');
} catch (error) {
// Log error but don't fail the request
console.error('Pixel error:', error);
return new Response('OK'); // Still return OK
}
}
Testing Pixels
The test_pixel type validates the entire pipeline:
// test-pixel-handler.js
export async function handleTestPixel(shop, data, env) {
// Write to multiple tables to verify connectivity
await env.DB.batch([
env.DB.prepare('INSERT INTO VisitorIdentity ...'),
env.DB.prepare('INSERT INTO SessionSnapshot ...'),
env.DB.prepare('INSERT INTO BotSignal ...'),
env.DB.prepare('INSERT INTO DailyMetrics ...'),
]);
return new Response(JSON.stringify({ success: true, timestamp: Date.now() }));
}
Monitoring
Key metrics to watch:
- Pixel volume: Requests/second to
/api/pixels - Error rate: Failed pixel writes
- Latency: Time from request to response
- D1 writes: Write operations per second