Examples and recipes
Complete queries for listings, product details, seller catalogs, and review pagination.
Last updated:
These recipes show how to compose real reads without replacing operations that remain in REST. Every example uses variables and sends x-Store-Domain.
Collection page
Combine collection metadata with its products in one request. The same $slug feeds both selections:
query CollectionPage($slug: String!, $page: Int = 1) {
collection(slug: $slug) {
id
name
description
imageUrl
productCount
}
products(
input: {
collections: [$slug]
page: $page
pageSize: 24
sort: DEFAULT
}
) {
pageIndex
totalPages
totalCount
products {
id
name
slug
imageUrl
price
compareAtPrice
isAvailable
}
filters {
id
label
type
values { value label count selected }
}
}
}{
"slug": "summer",
"page": 1
}To apply a facet, add values such as option:color:blue or attribute:material:cotton to input.filters. Read products for every limit.
Product page
A PDP normally needs two steps: first resolve the product by slug, then use its opaque ID for related products and reviews. GraphQL cannot use the result of one field as another field's argument within the same operation.
1. Details
query ProductPage($slug: String!) {
store { name currency }
product(slug: $slug) {
id
name
description
imageUrl
price
compareAtPrice
isAvailable
images { url name order }
variants {
id
sku
price
compareAtPrice
isAvailable
options { key name value }
}
seller { id name slug }
}
}2. Secondary data
Pass the previous product.id as $productId:
query ProductSecondary($productId: String!, $reviewsCursor: String) {
relatedProducts(productId: $productId, limit: 4) {
id
name
slug
imageUrl
price
isAvailable
}
productReviews(
productId: $productId
limit: 10
cursor: $reviewsCursor
sortBy: "createdOn"
) {
data {
id
rating
title
content
reviewerName
isVerifiedPurchase
createdOn
}
hasNextPage
nextCursor
totalCount
}
}This design lets you render primary information first and load recommendations or reviews lazily.
Seller catalog
Compose store identity, the seller profile, and its products in one operation:
query SellerCatalog($slug: String!, $pageSize: Int = 12) {
store {
id
name
currency
}
seller(slug: $slug) {
id
name
slug
logo
rating { average totalReviews }
deliveryPolicy
}
products(
input: {
sellerSlugs: [$slug]
pageSize: $pageSize
sort: NEWEST
}
) {
totalCount
products {
id
name
slug
imageUrl
price
isAvailable
}
}
}{
"slug": "acme",
"pageSize": 12
}Paginate reviews
Keep nextCursor as an opaque string. The first request uses null; each following request sends the returned cursor back:
const document = `
query ReviewPage($productId: String!, $cursor: String) {
productReviews(
productId: $productId
limit: 20
cursor: $cursor
sortBy: "createdOn"
) {
data {
id
rating
title
content
reviewerName
createdOn
}
hasNextPage
nextCursor
}
}
`;
async function loadReviewPage(productId, cursor = null) {
const response = await fetch("https://storefront.ecomiq.pe/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-Store-Domain": "my-store",
},
body: JSON.stringify({
query: document,
variables: { productId, cursor },
}),
});
const result = await response.json();
if (!response.ok || result.errors?.length) {
throw new Error(result.errors?.[0]?.message ?? `HTTP ${response.status}`);
}
return result.data.productReviews;
}
const firstPage = await loadReviewPage("456");
if (firstPage.hasNextPage) {
const secondPage = await loadReviewPage("456", firstPage.nextCursor);
console.log(secondPage.data);
}Do not increment, decode, or combine the cursor with different filters. When rating, search, or sorting changes, restart pagination with cursor: null.
Query with @ecomiq/storefront
The SDK validates the data object with Zod:
import { createClient, z } from "@ecomiq/storefront";
const CollectionPageData = z.object({
collection: z.object({
id: z.string(),
name: z.string(),
productCount: z.number(),
}),
products: z.object({
totalCount: z.number(),
products: z.array(
z.object({
id: z.string(),
name: z.string(),
slug: z.string(),
price: z.number(),
}),
),
}),
});
const api = createClient();
const data = await api.graphql(
`
query CollectionPage($slug: String!) {
collection(slug: $slug) { id name productCount }
products(input: { collections: [$slug], pageSize: 24 }) {
totalCount
products { id name slug price }
}
}
`,
CollectionPageData,
{ slug: "summer" },
);