Technical Guide

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.

MO

Technical Guide Expert

• 6 min read

JavaScript Node.js Personalization Server-side Express

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() and track()
  • 🔒 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:

  1. Monitor Performance: Track response times and conversion rates
  2. Implement A/B Testing: Use our dashboard to test different content variants
  3. Scale Your Implementation: Add personalization to more content pieces
  4. 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!

We use cookies to enhance your experience and analyze site usage. Learn more