GuidesSearch & discovery

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

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.

ParamMeaning
qThe search query. Blank/whitespace = no query (empty 200).
collectionRestrict results to a collection slug.
min_price_cents / max_price_centsPrice facet filter, integer cents. A non-integer or negative value is treated as unset.
limit / offsetPagination (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.v1 analytics 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.