Saltar al contenido
Guías

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:

CollectionPage.graphql
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 }
    }
  }
}
Variables
{
  "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

ProductPage.graphql
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:

ProductSecondary.graphql
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:

SellerCatalog.graphql
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
    }
  }
}
Variables
{
  "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:

reviews.js
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:

collection-page.ts
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" },
);

En esta página