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-sdkpackage 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:
- REST query shaping - map app-owned listing intent into the existing
client.products.getAll()QueryOptionscontract. - 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
pageandperPage. - 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
QueryOptionscontract. - 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.inStockInintentionally mirrors the existing backend option name.- The SDK does not rename
inStockInto warehouse IDs or a stronger warehouse-specific abstraction. - Slug filters are intentionally left out of
ProductListingQueryOptionsuntil the current low-levelQueryOptions.slugmismatch 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 orURLSearchParams.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:
- Parse the current listing URL with
parseProductListingUrlState(). - Apply a filter or sort change in app code.
- Use
toggleProductFilterValue()when the interaction is a multi-select filter toggle. - Serialize the next state back into a canonical URL.
- Map the app-owned listing intent into REST
QueryOptionswithtoProductQueryOptions()if the fetch still usesclient.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.