Skip to main content

List Products

This guide shows you how to fetch and display a list of products from your Elastic Path Commerce Cloud store.

tip

Before using the SDK to fetch products, make sure you've set up authentication. See the Authentication Quick Start guide to configure your credentials and SDK setup.

Basic product listing

Fetch all products

Use the getByContextAllProducts function to retrieve all products from your store:

import { getByContextAllProducts } from "@epcc-sdk/sdks-shopper"

const response = await getByContextAllProducts({
query: {
include: ["main_image"],
},
})

const products = response.data?.data || []
const productMainImages = response.data?.included?.main_images || []

The include parameter fetches product images alongside the basic product data, which you'll need for displaying product cards.

Extract product images

Use the extractProductImage helper to safely access product images:

import { extractProductImage } from "@epcc-sdk/sdks-shopper"

// Extract the main image for a product
const mainImage = extractProductImage(product, productMainImages)
const imageUrl = mainImage?.link?.href || "/placeholder.jpg"

// The extractProductImage function finds the matching image by ID:
const extractProductImage = (product, images) => {
return images?.find((file) => {
if (file.id === product.relationships?.main_image?.data?.id) {
return file
}
})
}

Handle empty states and errors

Always check for empty results and handle errors gracefully:

try {
const response = await getByContextAllProducts({
query: {
include: ["main_image"],
},
})

// Check for errors
if (!response.data) {
throw new Error("Failed to fetch products")
}

const products = response.data?.data || []
const productMainImages = response.data?.included?.main_images || []

// Handle empty results
if (products.length === 0) {
return { products: [], message: "No products found" }
}

return { products, productMainImages }
} catch (error) {
console.error("Error fetching products:", error)
throw new Error("Unable to load products. Please try again.")
}

Pagination

Add pagination

Handle large product catalogs with pagination using page[limit] and page[offset] parameters:

const page = 1 // Current page number
const limit = 20 // Items per page

const response = await getByContextAllProducts({
query: {
include: ["main_image"],
"page[limit]": BigInt(limit), // Maximum items to return (1-100)
"page[offset]": BigInt((page - 1) * limit), // Number of items to skip
},
})

// Extract pagination metadata from response
const resultsInfo = response.data?.meta?.results
const totalCount = Number(resultsInfo?.total) || 0
const totalPages = Math.ceil(totalCount / limit)
const hasNextPage = page < totalPages
const hasPreviousPage = page > 1

Pagination Options:

  • page[limit]: Maximum number of items to return (1-100, default: 25)
  • page[offset]: Number of items to skip for pagination
  • Response includes meta.results.total for total item count

Filtering

Add product filtering

Filter products using Elastic Path's filtering syntax with exact matches and multiple value support:

// Available filter options
const filters = {
name: "Blue Shirt", // Exact product name match
sku: "SHIRT-001", // Exact SKU match
productTypes: ["parent", "child"], // Match multiple product types
}

// Build filter query string
function buildFilterQuery(filters) {
const filterParts = []

// Name filter - exact match only
if (filters.name?.trim()) {
filterParts.push(`eq(name,${filters.name.trim()})`)
}

// SKU filter - exact match only
if (filters.sku?.trim()) {
filterParts.push(`eq(sku,${filters.sku.trim()})`)
}

// Product types - supports multiple values
if (filters.productTypes?.length > 0) {
if (filters.productTypes.length === 1) {
filterParts.push(`eq(product_types,${filters.productTypes[0]})`)
} else {
filterParts.push(`in(product_types,${filters.productTypes.join(',')})`)
}
}

return filterParts.length > 0 ? { filter: filterParts.join(',') } : {}
}

// Apply filters to API call
const filterQuery = buildFilterQuery(filters)
const response = await getByContextAllProducts({
query: {
include: ["main_image"],
...filterQuery,
},
})

Available Filters:

  • eq(name,value): Exact product name match
  • eq(sku,value): Exact SKU match
  • eq(product_types,value): Single product type match
  • in(product_types,value1,value2): Multiple product types match
  • Multiple filters combined with commas: eq(name,value),eq(sku,value)

Common Product Types:

  • parent: Parent products with variations
  • child: Child products (variations)
  • bundle: Product bundles
  • standalone: Individual products

Advanced usage

Combine pagination, filtering, and sorting

Use both pagination and filtering together for complete product catalog management:

export default async function ProductsPage({ searchParams }) {
const params = await searchParams

// Parse pagination parameters
const page = parseInt(params.page || "1", 10)
const limit = Math.min(parseInt(params.limit || "20", 10), 100) // Cap at 100

// Parse filter parameters from URL
const filters = {
name: params.name || "",
sku: params.sku || "",
productTypes: params.product_types ? params.product_types.split(',') : []
}

// Build filter query
const filterQuery = buildFilterQuery(filters)

// Fetch products with both pagination and filtering
const response = await getByContextAllProducts({
query: {
include: ["main_image"],
"page[limit]": BigInt(limit),
"page[offset]": BigInt((page - 1) * limit),
...filterQuery,
},
})

const products = response.data?.data || []
const productMainImages = response.data?.included?.main_images || []

// Extract pagination metadata
const resultsInfo = response.data?.meta?.results
const totalCount = Number(resultsInfo?.total) || 0
const totalPages = Math.ceil(totalCount / limit)

return (
<div>
<p>Showing {products.length} of {totalCount} products</p>
<p>Page {page} of {totalPages}</p>
{/* Render products and pagination controls */}
</div>
)
}

Key Benefits:

  • URL-based state: Pagination and filters persist in URL for sharing/bookmarking
  • Performance: Only fetch needed items with pagination
  • Flexibility: Combine any filters with pagination
  • Metadata: Access total counts and pagination info from API response

Performance optimization

Limit included resources

Only include the resources you need to improve performance:

// Only include main image for basic listing
const response = await getByContextAllProducts({
query: {
include: ["main_image"],
"page[limit]": BigInt(20),
},
})

Cache product data

For frequently accessed product lists, consider caching:

// Example with Next.js unstable_cache
import { unstable_cache } from 'next/cache'

const getCachedProducts = unstable_cache(
async () => {
const response = await getByContextAllProducts({
query: {
include: ["main_image"],
"page[limit]": BigInt(50),
},
})
return response.data
},
['products-list'],
{
revalidate: 300, // 5 minutes
tags: ['products']
}
)

User experience enhancements

Loading states

Show loading indicators while fetching products:

// React component example
function ProductList() {
const [products, setProducts] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)

useEffect(() => {
const fetchProducts = async () => {
try {
setLoading(true)
const response = await getByContextAllProducts({
query: { include: ["main_image"] },
})
setProducts(response.data?.data || [])
} catch (err) {
setError("Failed to load products")
} finally {
setLoading(false)
}
}

fetchProducts()
}, [])

if (loading) return <div>Loading products...</div>
if (error) return <div>Error: {error}</div>
if (products.length === 0) return <div>No products found</div>

return (
<div>
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
)
}

SEO optimization

Optimize product listing pages for search engines:

// Next.js metadata generation
export async function generateMetadata({ searchParams }) {
const params = await searchParams
const page = parseInt(params.page || "1", 10)
const filters = params.name || ""

let title = "Products"
let description = "Browse our product catalog"

if (filters) {
title = `${filters} - Products`
description = `Find ${filters} products in our catalog`
}

if (page > 1) {
title += ` - Page ${page}`
}

return {
title,
description,
robots: {
index: true,
follow: true,
},
}
}
tip

See the complete list-products example on GitHub for a full implementation with authentication, error handling, pagination, and filtering.