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:
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
mkdir ai-recommendations && cd ai-recommendations
npm init -y
npm install express cors dotenv openaiCreate a .env file:
QUBAX_API_KEY=your_api_key_here
QUBAX_BASE_URL=https://api.qubax.ai/v1
PORT=3001Step 2: Create Your Product Catalog
// 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:
// 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
// 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:
<!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:
// 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
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:
# 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.