Ejemplos y recetas
Consultas completas para listados, detalle de producto, catálogo de seller y paginación de reseñas.
Última actualización:
Estas recetas muestran cómo componer lecturas reales sin reemplazar las operaciones que continúan en REST. Todas usan variables y envían x-Store-Domain.
Página de colección
Combina metadata de colección con sus productos en una solicitud. El mismo $slug alimenta ambas selecciones:
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
}Para aplicar una faceta, agrega valores como option:color:blue o attribute:material:cotton a input.filters. Consulta todos los límites en products.
Página de producto
Una PDP normalmente necesita dos pasos: primero resuelve el producto por slug; después usa su ID opaco para relacionados y reseñas. GraphQL no permite usar el resultado de un campo como argumento de otro campo dentro de la misma operación.
1. Detalle
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. Datos secundarios
Pasa el product.id anterior como $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
}
}Este diseño permite renderizar la información principal primero y cargar recomendaciones o reseñas de forma diferida.
Catálogo de seller
Compón la identidad de la tienda, el perfil del seller y sus productos en una operación:
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
}Paginar reseñas
Conserva nextCursor como string opaco. La primera solicitud usa null; cada solicitud siguiente reenvía el cursor recibido:
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": "mi-tienda",
},
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);
}No incrementes, decodifiques ni combines el cursor con filtros distintos. Si cambian rating, search u orden, inicia la paginación de nuevo con cursor: null.
Consulta con @ecomiq/storefront
El SDK valida el objeto data con 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" },
);