Getting Started with Product Catalog
This guide walks you through the essential product catalog operations in the Nue Self-Service API. You'll learn how to retrieve product information, understand pricing structures, and build product catalogs for your applications.
Prerequisites
Before you begin, ensure you have:
- A valid Nue API key
- Access to the Nue Lifecycle Manager API
- Basic understanding of REST APIs and JSON
Authentication
All product catalog operations require authentication using your Nue API key in the nue-api-key header:
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");Retrieving Product Information
The product catalog API allows you to retrieve all published products or fetch specific products by ID. To learn more about publishing products, please read here.
Fetch All Products
Try it now: Fetch products API ā
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");
const requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
fetch("https://api.nue.io/catalog/products", requestOptions)
.then(response => response.json())
.then(result => {
console.log('Product catalog loaded:', result);
// Display each product
result.forEach(product => {
console.log(`Product: ${product.name}`);
console.log(`ID: ${product.id}`);
console.log(`Description: ${product.description}`);
console.log(`Price Book Entries: ${product.priceBookEntries.length}`);
console.log('---');
});
})
.catch(error => console.log('error', error));Fetch Specific Product
Try it now: Fetch products API ā
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");
// Fetch a specific product by ID
const productId = "01tE200000B8pYeIAJ";
const requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
fetch(`https://api.nue.io/catalog/products/${productId}`, requestOptions)
.then(response => response.json())
.then(product => {
console.log('Product details:', product);
console.log(`Product Name: ${product.name}`);
console.log(`SKU: ${product.sku}`);
console.log(`Status: ${product.status}`);
console.log(`Publish Status: ${product.publishStatus}`);
// Display pricing information
if (product.priceBookEntries && product.priceBookEntries.length > 0) {
console.log('Available Pricing:');
product.priceBookEntries.forEach(entry => {
console.log(` - ${entry.currencyIsoCode}: ${entry.listPrice}`);
console.log(` UOM: ${entry.uom.name}`);
});
}
})
.catch(error => console.log('error', error));Understanding Product Structure
Products in the Nue catalog have a rich data structure. Here's what you'll typically work with:
Core Product Fields
- id: Unique product identifier
- name: Display name for the product
- description: Detailed product description
- sku: Stock keeping unit identifier
- status: Product status (Active, Inactive, etc.)
- publishStatus: Whether the product is published for self-service
Pricing Information
Each product includes priceBookEntries that contain:
- listPrice: Base price for the product
- currencyIsoCode: Currency (USD, EUR, etc.)
- uom: Unit of measure (User/Month, License, etc.)
- active: Whether this pricing is currently active
Product Features and Options
- productFeatures: Available features for this product
- productOptions: Configurable options for bundles
- configurable: Whether the product has configurable options
Building a Product Catalog Display
Here's a practical example of building a product catalog for your application:
async function buildProductCatalog() {
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");
try {
const response = await fetch("https://api.nue.io/catalog/products", {
method: 'GET',
headers: myHeaders
});
const products = await response.json();
// Filter only published products
const publishedProducts = products.filter(product =>
product.publishStatus === 'Published' && product.status === 'Active'
);
// Build catalog display
const catalog = publishedProducts.map(product => ({
id: product.id,
name: product.name,
description: product.description,
sku: product.sku,
imageUrl: product.imageUrl,
pricing: product.priceBookEntries.map(entry => ({
price: entry.listPrice,
currency: entry.currencyIsoCode,
unitOfMeasure: entry.uom.name,
recommended: entry.recommended
})),
features: product.productFeatures || [],
isBundle: product.configurable,
bundleOptions: product.productOptions || []
}));
console.log('Catalog ready for display:', catalog);
return catalog;
} catch (error) {
console.error('Failed to load product catalog:', error);
throw error;
}
}
// Usage
buildProductCatalog()
.then(catalog => {
// Render your product catalog UI
catalog.forEach(product => {
console.log(`š¦ ${product.name}`);
console.log(` ${product.description}`);
product.pricing.forEach(price => {
const badge = price.recommended ? ' (Recommended)' : '';
console.log(` š° ${price.currency} ${price.price}/${price.unitOfMeasure}${badge}`);
});
if (product.isBundle) {
console.log(` š§ Configurable bundle with ${product.bundleOptions.length} options`);
}
console.log('');
});
});Filtering and Searching Products
For better user experience, you'll want to implement filtering and search:
function filterProducts(products, filters = {}) {
return products.filter(product => {
// Filter by name/description search
if (filters.search) {
const searchTerm = filters.search.toLowerCase();
const matchesName = product.name.toLowerCase().includes(searchTerm);
const matchesDescription = product.description?.toLowerCase().includes(searchTerm);
if (!matchesName && !matchesDescription) return false;
}
// Filter by price range
if (filters.minPrice || filters.maxPrice) {
const prices = product.priceBookEntries.map(entry => entry.listPrice);
const minProductPrice = Math.min(...prices);
const maxProductPrice = Math.max(...prices);
if (filters.minPrice && maxProductPrice < filters.minPrice) return false;
if (filters.maxPrice && minProductPrice > filters.maxPrice) return false;
}
// Filter by product type
if (filters.bundlesOnly && !product.configurable) return false;
if (filters.simpleProductsOnly && product.configurable) return false;
return true;
});
}
// Usage example
const allProducts = await buildProductCatalog();
// Search for "professional" products under $100
const filteredProducts = filterProducts(allProducts, {
search: 'professional',
maxPrice: 100
});
console.log(`Found ${filteredProducts.length} matching products`);Handling Bundle Products
Bundle products have special considerations for configuration and automatic order product creation.
Important Bundle Behavior
Critical: When ordering bundle products with required product options (add-ons):
- Explicitly configured add-ons are created exactly as specified in your order
- Required but unconfigured add-ons are automatically created with default quantities
- Optional add-ons are only created if explicitly included in the order
This ensures customers receive complete bundle functionality even if not all required components are explicitly configured in the order request. The system automatically provisions missing required components to maintain bundle integrity.
// Example: Bundle with required add-ons
const bundleOrder = {
"orderProducts": [
{
"priceBookEntryId": "01uEa00000F9JpQIAV", // Salesforce Price Book Entry ID
"quantity": 1,
"addOns": [
{
"productOptionId": "01oEa00000G8KqRIAV", // Salesforce Product Option ID
"productOptionQuantity": 2
}
// Note: If this bundle has required add-ons not listed here,
// they will be automatically created as separate order products
// with default quantities to ensure complete bundle functionality
]
}
]
};
// Result: Order creates multiple order products:
// 1. Main bundle product (as specified)
// 2. Optional add-on (as configured)
// 3. Required add-on A (auto-created with default quantity)
// 4. Required add-on B (auto-created with default quantity)Bundle Analysis and Configuration
function analyzeBundleProduct(product) {
if (!product.configurable || !product.productOptions) {
return { isBundle: false };
}
const bundleInfo = {
isBundle: true,
baseProduct: {
name: product.name,
description: product.description,
basePricing: product.priceBookEntries
},
options: product.productOptions.map(option => ({
id: option.id,
name: option.name,
description: option.description,
required: option.required,
optionType: option.optionType,
defaultQuantity: option.defaultQuantity || 1,
// Additional option details would be here
})),
requiredOptions: product.productOptions.filter(option => option.required),
optionalOptions: product.productOptions.filter(option => !option.required)
};
return bundleInfo;
}
// Example usage for bundle handling
async function displayBundleDetails(productId) {
const response = await fetch(`https://api.nue.io/catalog/products/${productId}`, {
method: 'GET',
headers: myHeaders
});
const product = await response.json();
const bundleInfo = analyzeBundleProduct(product);
if (bundleInfo.isBundle) {
console.log(`š¦ Bundle: ${bundleInfo.baseProduct.name}`);
if (bundleInfo.requiredOptions.length > 0) {
console.log('\nš“ Required Options (auto-created if not configured):');
bundleInfo.requiredOptions.forEach(option => {
console.log(` ⢠${option.name} (Default Qty: ${option.defaultQuantity})`);
console.log(` ${option.description}`);
});
}
if (bundleInfo.optionalOptions.length > 0) {
console.log('\nš¢ Optional Add-ons (only created if explicitly included):');
bundleInfo.optionalOptions.forEach(option => {
console.log(` ⢠${option.name}`);
console.log(` ${option.description}`);
});
}
console.log('\nš” Tip: Required options will be automatically added to orders even if not explicitly configured, ensuring complete bundle functionality.');
} else {
console.log(`š¦ Simple Product: ${product.name}`);
}
}Common Patterns and Best Practices
1. Error Handling
Always implement proper error handling for your product catalog operations:
async function safeProductFetch(productId = null) {
try {
const url = productId
? `https://api.nue.io/catalog/products/${productId}`
: 'https://api.nue.io/catalog/products';
const response = await fetch(url, {
method: 'GET',
headers: myHeaders
});
if (!response.ok) {
if (productId && response.status === 404) {
throw new Error(`Product ${productId} not found or not published`);
}
throw new Error(`HTTP error! status: ${response.status}`);
}
// /catalog/products returns an array of products
// /catalog/products/{productId} returns a single product object
return await response.json();
} catch (error) {
console.error('Product fetch error:', error);
return productId ? null : [];
}
}2. Caching Product Data
Product catalogs don't change frequently, so implement caching:
class ProductCatalogCache {
constructor(ttlMinutes = 30) {
this.cache = new Map();
this.ttl = ttlMinutes * 60 * 1000; // Convert to milliseconds
}
set(key, data) {
this.cache.set(key, {
data,
timestamp: Date.now()
});
}
get(key) {
const cached = this.cache.get(key);
if (!cached) return null;
// Check if cache has expired
if (Date.now() - cached.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
return cached.data;
}
async getProducts() {
const cached = this.get('all-products');
if (cached) {
console.log('Using cached product data');
return cached;
}
console.log('Fetching fresh product data');
const products = await safeProductFetch();
this.set('all-products', products);
return products;
}
}
// Usage
const catalogCache = new ProductCatalogCache(30); // 30-minute cache
const products = await catalogCache.getProducts();3. Performance Optimization
For large catalogs, implement pagination and lazy loading:
function paginateProducts(products, page = 1, pageSize = 20) {
const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + pageSize;
return {
products: products.slice(startIndex, endIndex),
totalProducts: products.length,
totalPages: Math.ceil(products.length / pageSize),
currentPage: page,
hasNextPage: endIndex < products.length,
hasPreviousPage: page > 1
};
}
// Usage
const allProducts = await catalogCache.getProducts();
const page1 = paginateProducts(allProducts, 1, 10);
console.log(`Showing ${page1.products.length} of ${page1.totalProducts} products`);Next Steps
Now that you understand basic product catalog operations, you can:
- Learn about pricing structures in the Product Data Model guide
- Understand publishing workflows for managing product availability
- Explore bundle configuration for complex product offerings
- Build shopping experiences with product selection and ordering
Move on to Product Data Model to learn about all available product fields and their relationships, or Key Considerations for Products to understand best practices for self-service product catalogs.