User Registration Flow
Copy
import { UserBoost } from "@userboost/sdk";
// Initialize once at app startup
UserBoost.init({
apiKey: process.env.USERBOOST_API_KEY,
debug: process.env.NODE_ENV === "development",
});
// Registration endpoint
app.post("/api/register", async (req, res) => {
const { email, password, name } = req.body;
try {
// Create user in your database
const user = await User.create({ email, password, name });
// Track registration event with user profile
UserBoost.event("user_registered", {
user: {
id: user.id,
email: user.email,
name: user.name,
traits: {
plan: "free",
created_at: user.createdAt.toISOString(),
},
},
properties: {
registration_source: "web",
},
});
res.json({ success: true, userId: user.id });
} catch (error) {
// Track failed registration
UserBoost.event("registration_failed", {
user: {
id: `visitor_${Date.now()}`,
},
properties: {
email: email,
error: error.message,
},
});
res.status(400).json({ error: error.message });
}
});
Onboarding Progress Tracking
Copy
// Track onboarding steps
app.post("/api/onboarding/:step", authenticateUser, async (req, res) => {
const { step } = req.params;
const { userId } = req.user;
const stepData = req.body;
// Update user progress in database
await User.updateOnboardingStep(userId, step, stepData);
// Track onboarding progress
UserBoost.event("onboarding_step_completed", {
user: {
id: userId,
},
properties: {
step: step,
progress: stepData.progress || 0,
time_spent: stepData.timeSpent || 0,
},
});
// Check if onboarding is complete
const user = await User.findById(userId);
if (user.onboardingComplete) {
UserBoost.event("onboarding_completed", {
user: {
id: userId,
},
properties: {
completion_time: Date.now() - user.createdAt.getTime(),
steps_completed: user.onboardingSteps.length,
},
});
}
res.json({ success: true });
});
Feature Usage Tracking
Copy
// Track feature usage with middleware
const trackFeatureUsage = (featureName) => {
return (req, res, next) => {
const startTime = Date.now();
// Continue with request
next();
// Track after response (don't block the response)
res.on("finish", () => {
if (req.user && res.statusCode === 200) {
UserBoost.event("feature_used", {
user: {
id: req.user.id,
},
properties: {
feature: featureName,
duration: Date.now() - startTime,
endpoint: req.path,
method: req.method,
},
});
}
});
};
};
// Apply to specific routes
app.get(
"/api/dashboard",
authenticateUser,
trackFeatureUsage("dashboard"),
(req, res) => {
// Your dashboard logic
res.json(dashboardData);
}
);
app.post(
"/api/projects",
authenticateUser,
trackFeatureUsage("create_project"),
(req, res) => {
// Your project creation logic
res.json({ success: true });
}
);
Subscription Events
Copy
// Track subscription changes
app.post("/api/upgrade", authenticateUser, async (req, res) => {
const { userId } = req.user;
const { plan, billingCycle } = req.body;
try {
// Process upgrade in your billing system
const subscription = await Stripe.createSubscription({
customer: userId,
plan: plan,
billing_cycle: billingCycle,
});
// Update user in database
await User.updatePlan(userId, plan);
// Track upgrade event with updated user traits
UserBoost.event("user_upgraded", {
user: {
id: userId,
traits: {
plan: plan,
billing_cycle: billingCycle,
upgraded_at: new Date().toISOString(),
},
},
properties: {
from_plan: req.user.plan,
to_plan: plan,
billing_cycle: billingCycle,
upgrade_value: getPlanValue(plan) - getPlanValue(req.user.plan),
},
});
res.json({ success: true, subscription });
} catch (error) {
UserBoost.event("upgrade_failed", {
user: {
id: userId,
},
properties: {
plan: plan,
error: error.message,
},
});
res.status(400).json({ error: error.message });
}
});
Batch Event Processing
Copy
// Batch events for high-volume applications
class UserBoostBatcher {
constructor() {
this.events = [];
this.batchSize = 50;
this.flushInterval = 5000; // 5 seconds
// Auto-flush periodically
setInterval(() => this.flush(), this.flushInterval);
}
add(eventName, userId, properties) {
this.events.push({
name: eventName,
userId: userId,
properties: properties,
timestamp: new Date().toISOString(),
});
// Flush if batch is full
if (this.events.length >= this.batchSize) {
this.flush();
}
}
async flush() {
if (this.events.length === 0) return;
const batch = this.events.splice(0);
try {
// Send batch to UserBoost
await Promise.all(
batch.map((event) =>
UserBoost.event(event.name, {
user: {
id: event.userId,
},
properties: event.properties,
})
)
);
} catch (error) {
console.error("Batch flush failed:", error);
// Could implement retry logic here
}
}
}
// Usage
const batcher = new UserBoostBatcher();
// Add events to batch instead of sending immediately
app.post("/api/action", authenticateUser, (req, res) => {
batcher.add("user_action", req.user.id, {
action: req.body.action,
context: req.body.context,
});
res.json({ success: true });
});
Error Handling and Resilience
Copy
// Graceful error handling wrapper
const safeTrack = async (eventName, userId, properties) => {
try {
await UserBoost.event(eventName, {
user: { id: userId },
properties: properties,
});
} catch (error) {
// Log error but don't break application flow
console.error("UserBoost tracking failed:", error);
// Optional: Store failed events for retry
await storeFailedEvent(eventName, userId, properties);
}
};
// Retry failed events
const retryFailedEvents = async () => {
const failedEvents = await getFailedEvents();
for (const event of failedEvents) {
try {
await UserBoost.event(event.name, {
user: {
id: event.userId,
},
properties: event.properties,
});
// Remove from failed events if successful
await removeFailedEvent(event.id);
} catch (error) {
// Keep in failed events for next retry
console.error("Retry failed for event:", event.id);
}
}
};
// Run retry every 5 minutes
setInterval(retryFailedEvents, 5 * 60 * 1000);
Testing Integration
Copy
// Mock UserBoost for testing
const mockUserBoost = {
init: jest.fn(),
event: jest.fn().mockResolvedValue({ success: true }),
identify: jest.fn().mockResolvedValue({ success: true }),
};
// Use in tests
jest.mock("@userboost/sdk", () => mockUserBoost);
// Test registration tracking
test("tracks user registration", async () => {
const response = await request(app)
.post("/api/register")
.send({ email: "test@example.com", password: "password" });
expect(response.status).toBe(200);
expect(mockUserBoost.event).toHaveBeenCalledWith("user_registered", {
user: {
id: expect.any(String),
},
properties: {
email: "test@example.com",
registration_source: "web",
},
});
});
Best Practices
- Initialize once at application startup
- Don’t block requests - track events asynchronously
- Handle failures gracefully - never break user flow for tracking
- Batch high-volume events to reduce API calls
- Use meaningful event names and consistent properties
- Test your tracking with mock implementations
- Monitor tracking health with logs and metrics
Performance Tips
- Use background workers for non-critical events
- Implement connection pooling for high-volume apps
- Cache user data to avoid repeated identify calls
- Use environment variables for configuration
- Monitor memory usage when batching events