Back to blog
Tutorial·9 min read·1601 words

How to Build an AI-Powered E-Commerce Recommendation System with Streaming Responses

A complete developer tutorial for building an AI-powered product recommendation system with real-time streaming responses. Includes Node.js code, frontend, and deployment tips.

How to Build an AI-Powered E-Commerce Recommendation System with Streaming Responses — illustration

How to Build an AI-Powered E-Commerce Recommendation System with Streaming Responses

E-commerce personalization is no longer optional — it's the difference between a thriving online store and one that bleeds customers. Studies consistently show that personalized product recommendations can increase revenue by 10-30% and improve customer retention significantly.

In this tutorial, we'll build a complete AI-powered recommendation system that uses large language models to generate personalized product suggestions with real-time streaming responses. We'll use a unified AI API so you can switch between GPT, Claude, and Gemini models without changing your code.

Prerequisites

  • Node.js 18+ or Python 3.10+
  • An API key from Qubax AI (gives you access to all major models)
  • Basic familiarity with async/await and HTTP APIs
  • A product catalog (we'll use a sample dataset)

Architecture Overview

Here's what we're building:

code
User Query → Product Search → LLM Analysis → Streaming Recommendations → Frontend Display
     ↑                                                              |
     └────────────── User Feedback Loop ←───────────────────────────┘

The key innovation is streaming — instead of waiting for the entire response to generate, we stream tokens to the client as they're produced. This creates a snappy, responsive experience that feels instant to users.

Step 1: Set Up Your Project

bash
mkdir ai-recommendations && cd ai-recommendations
npm init -y
npm install express cors dotenv openai

Create a .env file:

bash
QUBAX_API_KEY=your_api_key_here
QUBAX_BASE_URL=https://api.qubax.ai/v1
PORT=3001

Step 2: Create Your Product Catalog

javascript
// products.js
const products = [
  { id: 1, name: "Wireless Noise-Cancelling Headphones", category: "audio", price: 299, tags: ["premium", "wireless", "noise-cancelling"] },
  { id: 2, name: "Mechanical Gaming Keyboard", category: "peripherals", price: 149, tags: ["mechanical", "RGB", "gaming"] },
  { id: 3, name: "4K Webcam", category: "video", price: 89, tags: ["streaming", "4K", "auto-focus"] },
  { id: 4, name: "USB-C Docking Station", category: "accessories", price: 179, tags: ["connectivity", "dual-monitor", "100W"] },
  { id: 5, name: "Ergonomic Office Chair", category: "furniture", price: 449, tags: ["ergonomic", "lumbar-support", "mesh"] },
  { id: 6, name: "Portable SSD 2TB", category: "storage", price: 199, tags: ["fast", "portable", "USB-C"] },
  { id: 7, name: "Smart Desk Lamp", category: "accessories", price: 79, tags: ["LED", "adjustable", "wireless-charging"] },
  { id: 8, name: "Bluetooth Speaker", category: "audio", price: 129, tags: ["portable", "waterproof", "20h-battery"] },
];

module.exports = products;

Step 3: Build the Streaming Recommendation Engine

This is the heart of the system. We'll create a function that takes a user's query, searches the product catalog, and streams personalized recommendations:

javascript
// recommendations.js
const OpenAI = require('openai');
require('dotenv').config();

const client = new OpenAI({
  apiKey: process.env.QUBAX_API_KEY,
  baseURL: process.env.QUBAX_BASE_URL,
});

const products = require('./products');

async function* streamRecommendations(userQuery, userHistory = []) {
  // Step 1: Build context from product catalog
  const catalogContext = products
    .map(p => `- ID:${p.id} | ${p.name} | $${p.price} | Tags: ${p.tags.join(', ')}`)
    .join('\n');

  // Step 2: Include user history for personalization
  const historyContext = userHistory.length > 0
    ? `\nUser's recent purchases: ${userHistory.join(', ')}`
    : '';

  // Step 3: Construct the system prompt
  const systemPrompt = `You are an expert shopping assistant. Based on the user's request, recommend the most relevant products from this catalog:

${catalogContext}${historyContext}

Format each recommendation as:
**[Product Name]** - Brief personalized reason why this fits their needs. Include the price.

Provide 2-4 recommendations. Be conversational and helpful.`;

  // Step 4: Stream the response
  const stream = await client.chat.completions.create({
    model: 'gpt-4o-mini',  // Cost-effective, fast model
    messages: [
      { role: 'system', content: systemPrompt },
      { role: 'user', content: userQuery },
    ],
    stream: true,
    temperature: 0.7,
    max_tokens: 800,
  });

  for await (const chunk of stream) {
    const content = chunk.choices[0]?.delta?.content;
    if (content) {
      yield content;
    }
  }
}

module.exports = { streamRecommendations };

Step 4: Create the API Server

javascript
// server.js
const express = require('express');
const cors = require('cors');
require('dotenv').config();
const { streamRecommendations } = require('./recommendations');

const app = express();
app.use(cors());
app.use(express.json());

// Streaming endpoint using Server-Sent Events
app.post('/api/recommendations', async (req, res) => {
  const { query, history = [] } = req.body;

  if (!query) {
    return res.status(400).json({ error: 'Query is required' });
  }

  // Set up SSE headers
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  try {
    const stream = streamRecommendations(query, history);
    
    for await (const chunk of stream) {
      res.write(`data: ${JSON.stringify({ content: chunk })}\n\n`);
    }
    
    res.write('data: [DONE]\n\n');
    res.end();
  } catch (error) {
    console.error('Streaming error:', error);
    res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
    res.end();
  }
});

// Non-streaming fallback endpoint
app.post('/api/recommendations/sync', async (req, res) => {
  const { query, history = [] } = req.body;

  if (!query) {
    return res.status(400).json({ error: 'Query is required' });
  }

  try {
    let fullResponse = '';
    const stream = streamRecommendations(query, history);
    
    for await (const chunk of stream) {
      fullResponse += chunk;
    }
    
    res.json({ recommendations: fullResponse });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
  console.log(`Recommendation server running on port ${PORT}`);
});

Step 5: Build the Frontend Consumer

Here's a simple frontend that connects to the streaming endpoint and displays recommendations in real-time:

html
<!DOCTYPE html>
<html>
<head>
  <title>AI Shopping Assistant</title>
  <style>
    body { font-family: system-ui; max-width: 700px; margin: 50px auto; padding: 20px; }
    .chat-box { border: 1px solid #ddd; border-radius: 12px; padding: 20px; min-height: 300px; }
    .message { margin: 10px 0; line-height: 1.6; }
    input { width: 70%; padding: 12px; border: 1px solid #ccc; border-radius: 8px; }
    button { padding: 12px 24px; background: #4F46E5; color: white; border: none; border-radius: 8px; cursor: pointer; }
    .streaming { color: #4F46E5; }
  </style>
</head>
<body>
  <h1>AI Shopping Assistant</h1>
  <div class="chat-box" id="chat">
    <div class="message">Ask me for product recommendations!</div>
  </div>
  <div style="margin-top: 15px;">
    <input type="text" id="query" placeholder="I need a good setup for remote work..." />
    <button onclick="getRecommendations()">Ask AI</button>
  </div>

  <script>
    async function getRecommendations() {
      const query = document.getElementById('query').value;
      if (!query) return;
      
      const chat = document.getElementById('chat');
      const responseDiv = document.createElement('div');
      responseDiv.className = 'message streaming';
      chat.appendChild(responseDiv);
      
      const response = await fetch('/api/recommendations', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ query, history: [] }),
      });
      
      const reader = response.body.getReader();
      const decoder = new TextDecoder();
      let buffer = '';
      let displayedText = '';
      
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        
        buffer += decoder.decode(value);
        const lines = buffer.split('\n');
        buffer = lines.pop();
        
        for (const line of lines) {
          if (line.startsWith('data: ')) {
            const data = line.slice(6);
            if (data === '[DONE]') continue;
            try {
              const parsed = JSON.parse(data);
              displayedText += parsed.content;
              responseDiv.innerHTML = displayedText;
            } catch (e) {}
          }
        }
      }
      responseDiv.classList.remove('streaming');
    }
  </script>
</body>
</html>

Step 6: Add Advanced Personalization

To make recommendations truly personalized, add a user preference system:

javascript
// preferences.js

function buildPersonalizedPrompt(userPrefs, query) {
  const { budget, preferredCategories, pastPurchases, style } = userPrefs;
  
  let context = 'Personalization context:\n';
  if (budget) context += `- Budget: $${budget}\n`;
  if (preferredCategories?.length) context += `- Preferred categories: ${preferredCategories.join(', ')}\n`;
  if (pastPurchases?.length) context += `- Previously bought: ${pastPurchases.join(', ')}\n`;
  if (style) context += `- Style preference: ${style}\n`;
  
  return context;
}

// Example: Adjust model based on complexity
function selectModel(query) {
  const complexKeywords = ['compare', 'detailed', 'professional', 'enterprise'];
  const isComplex = complexKeywords.some(k => query.toLowerCase().includes(k));
  
  // Use a more powerful model for complex queries, cheaper one for simple ones
  return isComplex ? 'claude-sonnet-4-20250514' : 'gpt-4o-mini';
}

module.exports = { buildPersonalizedPrompt, selectModel };

Step 7: Add Error Handling and Rate Limiting

javascript
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 50, // 50 requests per window
  message: 'Too many requests, please try again later.',
});

app.use('/api/', limiter);

// Graceful error handling in the stream
process.on('unhandledRejection', (error) => {
  console.error('Unhandled promise rejection:', error);
});

Testing Your System

Start the server and test with curl:

bash
# Start the server
node server.js

# Test streaming endpoint
curl -N -X POST http://localhost:3001/api/recommendations \
  -H "Content-Type: application/json" \
  -d '{"query": "I need a home office setup for under $500"}'

You should see recommendations stream in token by token — exactly like ChatGPT's interface.

Performance Tips

  • Use gpt-4o-mini or equivalent for most recommendation queries — it's fast and cheap
  • Cache common queries to avoid redundant API calls
  • Limit max_tokens to control response length and cost
  • Batch multiple user queries during off-peak hours for bulk processing
  • Monitor API costs — streaming doesn't change pricing, but it makes costs feel invisible

FAQ

What AI model should I use for product recommendations?

For most e-commerce recommendation tasks, a fast and cost-effective model like GPT-4o-mini or Claude Haiku is ideal. Use more powerful models only for complex comparison queries.

How much does it cost to run an AI recommendation system?

With a model like GPT-4o-mini, each recommendation request typically costs less than $0.001. For a store with 10,000 daily visitors, that's roughly $10/day in API costs.

Can I use this with my existing e-commerce platform?

Yes! The API is platform-agnostic. Simply replace the sample product catalog with your actual product database and integrate the streaming endpoint into your frontend.

What's the benefit of streaming responses?

Streaming creates a much better user experience — users see recommendations appearing in real-time rather than waiting for a complete response. This reduces perceived latency and keeps users engaged.

How do I handle users who don't have purchase history?

For new users, you can use category preferences, browsing behavior, or demographic data to provide initial recommendations. As they interact more, the system naturally becomes more personalized.


Ready to build AI-powered features for your application? Qubax AI provides a unified API to access GPT, Claude, Gemini, and dozens of other models — with streaming support out of the box. Explore our models to find the right fit for your use case, or read our documentation for more tutorials.

Article tags

#AI tutorial#streaming#e-commerce#recommendation system#API
Share:Post on XTelegramLinkedInYHacker NewsReddit
Qubax AI

Qubax AI

AI Models at up to 99% off · Pay with crypto

Access GPT, Claude, Gemini, GLM & 340+ models through one OpenAI-compatible API. Up to 99% off. Pay with 200+ cryptocurrencies. Get $1 free credits — no credit card needed.

Related articles