Skip to main content
The product service is the catalogue lookup used by blocks that show product info — upsell cards, swap pickers, variant selectors. It returns products with their full variant list and purchase options.

Setup

import {
  provideContext,
  createProductService,
  createApiProductAdapter,
  ProductServiceContext,
} from "@juo/blocks";

provideContext(
  root,
  ProductServiceContext,
  createProductService(createApiProductAdapter({ baseUrl: "/api/v1" })),
);

Shape

type Product = {
  id: string;
  title: string;
  featuredImage: Image | null;
  hasOnlyDefaultVariant: boolean;
  collections: { handle: string }[];
  metadata?: Record<string, unknown>;
};

type ProductVariant = {
  id: string;
  title: string;
  image: Image | null;
  price: PriceWithCurrency;
  subscriptionPrice?: PriceWithCurrency;
  purchaseOptions?: PurchaseOption[];
  metadata?: Record<string, unknown>;
};

type WithVariants<T> = T & { variants: PaginatedList<ProductVariant> };

type ProductService<T extends WithVariants<Product> = WithVariants<Product>> = {
  getById(id: string): Promise<T | null>;
  getByVariantId(variantId: string): Promise<T | null>;
  search(opts?: {
    query?: string;
    pagination?: { before: string } | { after: string };
  }): Promise<PaginatedList<T>>;
};
The service is generic over T so adapters can return extended product types (with custom metadata or computed fields) while keeping the base shape intact.

Example: variant lookup

import { injectContext, ProductServiceContext } from "@juo/blocks";

async function loadProduct(el: HTMLElement, id: string) {
  const productService = injectContext(el, ProductServiceContext);
  const product = await productService.getById(id);
  if (!product) return;

  for (const variant of product.variants.items) {
    console.log(variant.title, variant.price);
  }
}
const { items, pagination } = await productService.search({
  query: "tag:featured",
  pagination: { after: lastCursor },
});
Search returns a PaginatedList<Product> — use pagination to drive infinite scroll or load-more affordances.