Add search and faceted browse
The public catalog exposes two discovery surfaces: full-text search with
price and collection facets, and collections for merchandised browse. Both
are unauthenticated reads over the same catalog as
GET /v1/tenants/:tenant/catalog/products — no operator token, no tenant
credential — and both are gated on the store being live and on products being
active. A failed read is always a 503, never an empty 200.
This guide uses the origin https://api.ruo.pro and the tenant nova-peptide.
Steps
Run a search
GET /v1/tenants/:tenant/catalog/search takes a query and optional facet
filters. An empty or whitespace-only q is a valid request meaning “no
query” — it returns an empty result set with a 200, not an error.
| Param | Meaning |
|---|---|
q | The search query. Blank/whitespace = no query (empty 200). |
collection | Restrict results to a collection slug. |
min_price_cents / max_price_cents | Price facet filter, integer cents. A non-integer or negative value is treated as unset. |
limit / offset | Pagination (limit 1-50, offset >= 0), clamped to safe bounds. |
curl "https://api.ruo.pro/v1/tenants/nova-peptide/catalog/search?q=bpc&collection=peptides&min_price_cents=1000&limit=24"The response echoes the query, the applied filters, the facet counts, and the matched products (each with variants):
{
"query": "bpc",
"result_count": 3,
"limit": 24,
"offset": 0,
"filters": { "priceMinCents": 1000, "priceMaxCents": null, "collectionSlug": "peptides" },
"facets": {
"price_buckets": [{ "min_cents": 0, "max_cents": 5000, "count": 2 }],
"collections": [{ "slug": "peptides", "title": "Peptides", "count": 3 }]
},
"products": [{ "id": "prod_123", "slug": "bpc-157", "name": "BPC-157", "variants": [] }]
}Render facets.price_buckets and facets.collections as clickable filters, and
feed the selection back as the query params on the next request.
Search with the typed SDK
The storefront client exposes search(). Return types flow from the API’s
OpenAPI document, so result_count, facets, and products are all typed.
import { createStorefrontClient } from "@dscodotco/sdk";
const store = createStorefrontClient({
apiUrl: "https://api.ruo.pro",
tenant: "nova-peptide",
storefrontToken: process.env.FLIGHTDECK_STOREFRONT_TOKEN!, // server-side only
});
const results = await store.search({ q: "bpc", collection: "peptides", limit: 24 });
console.log(results.result_count, results.facets.collections);Known drift: the SDK’s generated types name the price-filter params
price_min_cents / price_max_cents, but the handler reads
min_price_cents / max_price_cents. Until the spec is corrected, apply
price facets over the raw query string (the curl form above), and use the SDK
for q, collection, limit, and offset, which match.
List collections
GET /v1/tenants/:tenant/catalog/collections returns the store’s collections —
each a slug, title, and description. A collection has no status of its
own; the live-store gate plus the active-product filter are its visibility
contract.
curl "https://api.ruo.pro/v1/tenants/nova-peptide/catalog/collections"{ "collections": [{ "slug": "peptides", "title": "Peptides", "description": null }] }With the SDK:
const { collections } = await store.collections.list();Fetch one collection and its products
GET /v1/tenants/:tenant/catalog/collections/:slug returns the collection plus
its active products (each with variants) — the payload a collection landing page
renders directly.
curl "https://api.ruo.pro/v1/tenants/nova-peptide/catalog/collections/peptides"const { collection, products } = await store.collections.get("peptides");An unknown slug is a 404; a failed variant read is a 503, never a silently
empty collection.
Notes on honest behavior
- A successful search fires a fire-and-forget
commerce.product.searched.v1analytics signal. It has no in-repo consumer today (an allowlisted external sink), so it can never fail or slow the search read. - Search ranking and partial-token matching run in Postgres over the catalog’s full-text and trigram indexes — there is no separate search service to provision.
Related
- Collections & faceted search reference — the full route map, manifest gates, and seams.
- Quickstart — your first authenticated request and catalog read.
- Add an image to a product — the imagery that renders in search and collection results.