Product listing helpers

Use the TypeScript SDK product-listing query and URL-state helpers while keeping exact signatures in package reference.

Use this guide when a TypeScript app or shared server/runtime layer needs package-backed helpers for storefront-style listing queries or canonical listing URL state.

These helpers live on the root @enadhq/enad-ts-sdk package surface. Use package reference for exact helper signatures, option shapes, and return types. This guide explains when to use the helpers and what they intentionally do not own.

Two separate jobs

The helper family covers two different responsibilities:

  1. REST query shaping - map app-owned listing intent into the existing client.products.getAll() QueryOptions contract.
  2. URL state normalization - parse, update, and serialize canonical storefront listing query strings.

Use these helpers for query shaping and URL-state normalization. Fetching, React UI state, Search GraphQL variables, CMS data, and route rendering stay with their owning app or API layer.

Use toProductQueryOptions() for REST-style listing requests

Use toProductQueryOptions() when your app wants a storefront-friendly listing shape but still calls the REST-style products API through client.products.getAll().

import {
  toProductQueryOptions,
  type ProductListingQueryOptions,
} from "@enadhq/enad-ts-sdk";

const listingOptions: ProductListingQueryOptions = {
  pagination: { page: 2, perPage: 24 },
  search: "chair",
  stock: { inStock: true, inStockIn: "storefront" },
  include: {
    brands: ["hay"],
    categories: ["chairs"],
  },
  price: { min: 100, max: 500 },
};

const [products, error] = await client.products.getAll(
  toProductQueryOptions(listingOptions),
);

Reach for ProductListingQueryOptions when the calling code wants a clearer app-facing shape for common listing inputs.

  • Use it for pagination with page and perPage.
  • Use it for text search and sort.
  • Use it for active-only or child-category inclusion flags.
  • Use it for stock filters.
  • Use it for include/exclude lists for brands, categories, collections, tags, series, and related listing dimensions.
  • Use it for min/max price bounds.

Boundary notes for REST query shaping

toProductQueryOptions() is intentionally narrow:

  • It is a pure mapper to the current REST QueryOptions contract.
  • It does not fetch products.
  • It does not define Search GraphQL variables or response fields.
  • It does not own cache policy, routing, CMS behavior, or pagination UI.

Current package boundaries worth preserving:

  • stock.inStockIn intentionally mirrors the existing backend option name.
  • The SDK does not rename inStockIn to warehouse IDs or a stronger warehouse-specific abstraction.
  • Slug filters are intentionally left out of ProductListingQueryOptions until the current low-level QueryOptions.slug mismatch is resolved.

If the job is already Search GraphQL-first, route to Search latest or @enadhq/enad-ts-sdk/search instead of forcing Search semantics into REST helper docs.

Use the URL-state helpers for canonical listing query strings

Use parseProductListingUrlState(), serializeProductListingUrlState(), and toggleProductFilterValue() when your app wants framework-neutral handling for listing URLs. The helpers keep URL parsing separate from fetching and UI rendering.

import {
  parseProductListingUrlState,
  serializeProductListingUrlState,
  toggleProductFilterValue,
} from "@enadhq/enad-ts-sdk";

const state = parseProductListingUrlState(
  "?q=chair&f.brand=hay&f.brand=muuto&page=2",
);
const next = toggleProductFilterValue(state, "brand", "stolab");
const href = serializeProductListingUrlState(next);

The helpers normalize these SDK-owned query conventions:

  • scalar params: q, sort, page, perPage
  • price params: price.min, price.max
  • filter params: f.

Unknown params are preserved so app-owned campaign, preview, or tracking params can round-trip without the SDK claiming ownership of them.

Boundary notes for URL state

These helpers intentionally stop at query-string state:

  • They do not fetch products.
  • They do not define facet payloads.
  • They do not own React state, router behavior, or browser history.
  • They do not decide which filters should exist on the page.

Behavior to know:

  • parseProductListingUrlState() accepts either a query string or URLSearchParams.
  • serializeProductListingUrlState() emits the canonical SDK query-string form.
  • toggleProductFilterValue() removes pagination from the returned state so the app can reset to the first page after a filter change.

Typical flow

A common server/shared flow looks like this:

  1. Parse the current listing URL with parseProductListingUrlState().
  2. Apply a filter or sort change in app code.
  3. Use toggleProductFilterValue() when the interaction is a multi-select filter toggle.
  4. Serialize the next state back into a canonical URL.
  5. Map the app-owned listing intent into REST QueryOptions with toProductQueryOptions() if the fetch still uses client.products.getAll().

That keeps URL ownership, route updates, and network requests in application code while reusing the SDK's canonical parsing and mapping rules.

When not to use these helpers

Choose another route when the problem is actually about a different contract surface.

  • For Search GraphQL query shape, operation documents, facets, or response fields, use Search latest.
  • For shopper-facing PLP or autocomplete UI composition, use React product listing and search.
  • For exact REST endpoint fields or request/response schemas, use APIs, API reference, and OpenAPI reference.

Product listing helpers

# Product listing helpers Use this guide when a TypeScript app or shared server/runtime layer needs package-backed helpers for storefront-style listing queries or canonical listing URL state. > These helpers live on the root `@enadhq/enad-ts-sdk` package surface. Use package reference for exact helper signatures, option shapes, and return types. This guide explains when to use the helpers and what they intentionally do not own. ## Two separate jobs The helper family covers two different responsibilities: 1. **REST query shaping** - map app-owned listing intent into the existing `client.products.getAll()` `QueryOptions` contract. 2. **URL state normalization** - parse, update, and serialize canonical storefront listing query strings. Use these helpers for query shaping and URL-state normalization. Fetching, React UI state, Search GraphQL variables, CMS data, and route rendering stay with their owning app or API layer. ## Use `toProductQueryOptions()` for REST-style listing requests Use `toProductQueryOptions()` when your app wants a storefront-friendly listing shape but still calls the REST-style products API through `client.products.getAll()`. ```ts import { toProductQueryOptions, type ProductListingQueryOptions, } from "@enadhq/enad-ts-sdk"; const listingOptions: ProductListingQueryOptions = { pagination: { page: 2, perPage: 24 }, search: "chair", stock: { inStock: true, inStockIn: "storefront" }, include: { brands: ["hay"], categories: ["chairs"], }, price: { min: 100, max: 500 }, }; const [products, error] = await client.products.getAll( toProductQueryOptions(listingOptions), ); ``` Reach for `ProductListingQueryOptions` when the calling code wants a clearer app-facing shape for common listing inputs. - Use it for pagination with `page` and `perPage`. - Use it for text search and sort. - Use it for active-only or child-category inclusion flags. - Use it for stock filters. - Use it for include/exclude lists for brands, categories, collections, tags, series, and related listing dimensions. - Use it for min/max price bounds. ### Boundary notes for REST query shaping `toProductQueryOptions()` is intentionally narrow: - It is a **pure mapper** to the current REST `QueryOptions` contract. - It does **not** fetch products. - It does **not** define Search GraphQL variables or response fields. - It does **not** own cache policy, routing, CMS behavior, or pagination UI. Current package boundaries worth preserving: - `stock.inStockIn` intentionally mirrors the existing backend option name. - The SDK does **not** rename `inStockIn` to warehouse IDs or a stronger warehouse-specific abstraction. - Slug filters are intentionally left out of `ProductListingQueryOptions` until the current low-level `QueryOptions.slug` mismatch is resolved. If the job is already Search GraphQL-first, route to [Search latest](/search/latest) or `@enadhq/enad-ts-sdk/search` instead of forcing Search semantics into REST helper docs. ## Use the URL-state helpers for canonical listing query strings Use `parseProductListingUrlState()`, `serializeProductListingUrlState()`, and `toggleProductFilterValue()` when your app wants framework-neutral handling for listing URLs. The helpers keep URL parsing separate from fetching and UI rendering. ```ts import { parseProductListingUrlState, serializeProductListingUrlState, toggleProductFilterValue, } from "@enadhq/enad-ts-sdk"; const state = parseProductListingUrlState( "?q=chair&f.brand=hay&f.brand=muuto&page=2", ); const next = toggleProductFilterValue(state, "brand", "stolab"); const href = serializeProductListingUrlState(next); ``` The helpers normalize these SDK-owned query conventions: - scalar params: `q`, `sort`, `page`, `perPage` - price params: `price.min`, `price.max` - filter params: `f.` Unknown params are preserved so app-owned campaign, preview, or tracking params can round-trip without the SDK claiming ownership of them. ### Boundary notes for URL state These helpers intentionally stop at query-string state: - They do **not** fetch products. - They do **not** define facet payloads. - They do **not** own React state, router behavior, or browser history. - They do **not** decide which filters should exist on the page. Behavior to know: - `parseProductListingUrlState()` accepts either a query string or `URLSearchParams`. - `serializeProductListingUrlState()` emits the canonical SDK query-string form. - `toggleProductFilterValue()` removes pagination from the returned state so the app can reset to the first page after a filter change. ## Typical flow A common server/shared flow looks like this: 1. Parse the current listing URL with `parseProductListingUrlState()`. 2. Apply a filter or sort change in app code. 3. Use `toggleProductFilterValue()` when the interaction is a multi-select filter toggle. 4. Serialize the next state back into a canonical URL. 5. Map the app-owned listing intent into REST `QueryOptions` with `toProductQueryOptions()` if the fetch still uses `client.products.getAll()`. That keeps URL ownership, route updates, and network requests in application code while reusing the SDK's canonical parsing and mapping rules. ## When not to use these helpers Choose another route when the problem is actually about a different contract surface. - For Search GraphQL query shape, operation documents, facets, or response fields, use [Search latest](/search/latest). - For shopper-facing PLP or autocomplete UI composition, use [React product listing and search](/react-sdk/latest/guides/product-listing-search). - For exact REST endpoint fields or request/response schemas, use [APIs](/apis), API reference, and OpenAPI reference. ## Related routes - [TypeScript SDK latest](/ts-sdk/latest) - [Search latest](/search/latest) - [React SDK latest](/react-sdk/latest) - [React product listing and search](/react-sdk/latest/guides/product-listing-search) - [TypeScript SDK agent guide](/ai/ts-sdk/latest)