Setting Up Usertune's JavaScript Library in Node.js Applications
Complete guide to integrating Usertune's personalization library in Node.js and server-side JavaScript applications. Learn installation, configuration, and real-world e-commerce examples.
Technical Guide Expert
• 6 min read
Usertune's JavaScript library provides powerful personalization capabilities for Node.js applications with a clean, type-safe interface. Whether you're building an Express.js API, a Next.js application, or any server-side JavaScript project, our library integrates seamlessly into your existing architecture.
Why Choose Usertune for Node.js?
- 🎯 Simple API - Just two main methods:
content()
andtrack()
- 🔒 Type-Safe - Full TypeScript support with comprehensive type definitions
- ⚡ Modern - ES modules, async/await, built for modern JavaScript
- 🛡️ Server-Side Security - Handle access tokens securely on the server
- 📈 Scalable - Designed for high-traffic production applications
- 🔄 Smart Tracking - Automatic variant ID management for conversion tracking
Installation
Install the library using npm in your Node.js project:
npm install usertune.js
For TypeScript projects, types are included out of the box:
# TypeScript support is built-in
npm install usertune.js
# No need for @types/usertune.js
Basic Setup
Environment Configuration
First, set up your environment variables for secure token management:
# .env
USERTUNE_WORKSPACE_ID=your-workspace-id
USERTUNE_ACCESS_TOKEN=your-access-token
Initialize the Client
// services/personalization.js
import { Usertune } from 'usertune.js';
const client = new Usertune({
workspace: process.env.USERTUNE_WORKSPACE_ID,
accessToken: process.env.USERTUNE_ACCESS_TOKEN,
timeout: 10000,
debug: process.env.NODE_ENV === 'development'
});
export default client;
Configuration Options
The Usertune client accepts several configuration options perfect for server environments:
const client = new Usertune({
workspace: 'your-workspace-id', // Required: Your workspace identifier
accessToken: 'your-access-token', // Required for private content
timeout: 10000, // Request timeout (default: 10000ms)
debug: false // Enable debug logging in development
});
Content Retrieval
Basic Content Retrieval
// controllers/homeController.js
import usertuneClient from '../services/personalization.js';
export async function getHomepage(req, res) {
try {
const content = await usertuneClient.content('hero-banner');
res.render('homepage', {
title: content.data.title,
description: content.data.description,
cta: content.data.cta_text
});
} catch (error) {
console.error('Personalization failed:', error);
// Fallback to default content
res.render('homepage', getDefaultContent());
}
}
Advanced Personalization with User Context
Pass user attributes from your session or database:
export async function getPersonalizedHomepage(req, res) {
const user = req.user; // From your authentication middleware
try {
const content = await usertuneClient.content('hero-banner', {
user_tier: user.subscriptionTier,
purchase_history: user.lastPurchaseCategory,
location: user.country,
device_type: req.device.type,
time_of_day: new Date().getHours(),
user_segment: user.segment
});
res.json({
content: content.data,
variantId: content.metadata.variant_id
});
} catch (error) {
res.status(500).json({ error: 'Personalization unavailable' });
}
}
Conversion Tracking
Track user conversions server-side for accurate attribution:
// controllers/conversionController.js
import usertuneClient from '../services/personalization.js';
export async function trackPurchase(req, res) {
const { orderId, orderValue } = req.body;
try {
// Track the conversion
await usertuneClient.track('purchase', orderValue);
// Your existing order processing logic
const order = await processOrder(orderId);
res.json({
success: true,
message: 'Purchase tracked successfully',
order
});
} catch (error) {
console.error('Failed to track purchase:', error);
// Don't fail the order if tracking fails
res.json({ success: true, order });
}
}
Real-World Example: E-commerce Personalization Service
Here's a complete implementation for an e-commerce application:
// services/PersonalizationService.js
import { Usertune } from 'usertune.js';
class PersonalizationService {
constructor() {
this.client = new Usertune({
workspace: process.env.USERTUNE_WORKSPACE_ID,
accessToken: process.env.USERTUNE_ACCESS_TOKEN,
timeout: 5000
});
}
async getPersonalizedHomepage(user, device, location) {
try {
// Get multiple personalized content pieces
const [heroBanner, productRecs, promoBar] = await Promise.all([
this.client.content('hero-banner', {
user_tier: user.tier,
purchase_history: user.lastPurchaseCategory,
location: location.country,
device_type: device.type,
time_of_day: new Date().getHours()
}),
this.client.content('product-recommendations', {
browsing_history: user.recentlyViewed,
price_range: user.preferredPriceRange,
brand_preferences: user.favoriteBrands
}),
this.client.content('promo-bar', {
user_tier: user.tier,
location: location.country,
first_visit: user.isFirstVisit
})
]);
return {
hero: heroBanner.data,
products: productRecs.data,
promo: promoBar.data,
variantIds: {
hero: heroBanner.metadata.variant_id,
products: productRecs.metadata.variant_id,
promo: promoBar.metadata.variant_id
}
};
} catch (error) {
console.error('Personalization failed:', error);
return this.getFallbackContent();
}
}
async trackConversion(type, value, metadata = {}) {
try {
await this.client.track(type, value);
console.log(`Tracked ${type}: $${value}`);
} catch (error) {
console.error(`Failed to track ${type}:`, error);
}
}
getFallbackContent() {
return {
hero: {
title: "Welcome to Our Store",
description: "Discover amazing products",
cta_text: "Shop Now"
},
products: [],
promo: {
message: "Free shipping on orders over $50"
}
};
}
}
export default new PersonalizationService();
Express.js Route Implementation
// routes/api.js
import express from 'express';
import PersonalizationService from '../services/PersonalizationService.js';
import { authenticateUser } from '../middleware/auth.js';
const router = express.Router();
// Homepage personalization
router.get('/homepage', authenticateUser, async (req, res) => {
const user = req.user;
const device = req.device;
const location = req.location;
const personalizedContent = await PersonalizationService
.getPersonalizedHomepage(user, device, location);
res.json(personalizedContent);
});
// Track conversions
router.post('/track/:conversionType', authenticateUser, async (req, res) => {
const { conversionType } = req.params;
const { value, metadata } = req.body;
await PersonalizationService.trackConversion(conversionType, value, metadata);
res.json({ success: true });
});
export default router;
Error Handling Best Practices
Implement robust error handling for production applications:
class PersonalizationService {
async getContentWithFallback(contentId, attributes = {}) {
const MAX_RETRIES = 3;
const RETRY_DELAY = 1000;
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
return await this.client.content(contentId, attributes);
} catch (error) {
if (error.status === 404) {
// Content not found - return fallback immediately
return this.getFallbackContent(contentId);
}
if (error.status === 429 && attempt < MAX_RETRIES) {
// Rate limited - wait and retry
await this.delay(RETRY_DELAY * attempt);
continue;
}
if (attempt === MAX_RETRIES) {
console.error(`Failed to get content after ${MAX_RETRIES} attempts:`, error);
return this.getFallbackContent(contentId);
}
}
}
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
Performance Optimization
Caching Strategy
import NodeCache from 'node-cache';
class PersonalizationService {
constructor() {
this.client = new Usertune({
workspace: process.env.USERTUNE_WORKSPACE_ID,
accessToken: process.env.USERTUNE_ACCESS_TOKEN
});
// Cache content for 5 minutes
this.cache = new NodeCache({ stdTTL: 300 });
}
async getCachedContent(contentId, attributes) {
const cacheKey = `${contentId}_${JSON.stringify(attributes)}`;
let content = this.cache.get(cacheKey);
if (content) {
return content;
}
content = await this.client.content(contentId, attributes);
this.cache.set(cacheKey, content);
return content;
}
}
Testing
Unit Testing with Jest
// __tests__/PersonalizationService.test.js
import PersonalizationService from '../services/PersonalizationService.js';
// Mock the Usertune client
jest.mock('usertune.js', () => ({
Usertune: jest.fn().mockImplementation(() => ({
content: jest.fn(),
track: jest.fn()
}))
}));
describe('PersonalizationService', () => {
it('should return personalized content', async () => {
const mockContent = {
data: { title: 'Welcome!' },
metadata: { variant_id: 'abc123' }
};
PersonalizationService.client.content.mockResolvedValue(mockContent);
const result = await PersonalizationService.getPersonalizedHomepage(
{ tier: 'premium' },
{ type: 'desktop' },
{ country: 'US' }
);
expect(result.hero.title).toBe('Welcome!');
});
});
Next Steps
Now that you have Usertune integrated into your Node.js application:
- Monitor Performance: Track response times and conversion rates
- Implement A/B Testing: Use our dashboard to test different content variants
- Scale Your Implementation: Add personalization to more content pieces
- Optimize Caching: Fine-tune your caching strategy based on traffic patterns
For browser-side implementation, check out our companion guide: "Loading Personalized Content in the Browser with Usertune".
Ready to get started? Sign up for Usertune and begin personalizing your Node.js application today!