Skip to content
BolderBolder Developers
Esc
↑↓navigate↵open⌘Jpreview
On this page

Catalog & Search

Listing and looking up products, and live typeahead search over WebSocket.

Products

storefront.products — list/filter the catalog and look up individual products.

storefront.products.get({ q: 'shoes', page: 2 }, function(err, data) {
  if (err) return console.error(err)
  console.log(data.products, data.total_pages)
})

storefront.products.getForCart('aurora-runner', function(err, product) {
  if (err) return console.error(err)
  // product.variant_options / product.variant_list — the color/size matrix,
  // same shape product-variant-selector (see Components) expects
})
  • get(params?, fn) — List products, optionally filtered (q, collections, product_types, vendors, tags, price_gte, price_lte, page, facets). Calls fn(err, data) with data: { products, page, per_page, total_items, total_pages, facets }. Each product is in basic mode — no variant data.
  • related(productSlug, fn) — Products related to the given one (by tag or collection), for cross-sell widgets. Items already in the cart are excluded automatically. Calls fn(err, products).
  • getForCart(productSlug, fn) — A single product in cart-ready shape: the full variant_options/variant_list matrix (for multi-axis variant selection), bundle_config, modifiers, subscription_fees. This is the only way to get variant data headlessly — get()’s list endpoint never includes it, even in full mode. Calls fn(err, product).

storefront.search gives you live, typeahead-style product search over a WebSocket connection to Bolder’s hosted search service — the same backend that powers hosted themes’ search boxes.

bindTo(input, opts?) — the common case

Wires an <input> element up end to end: connects on focus (so the connection is warm before the first keystroke), debounces input, and emits results. Returns an Emitter — call .destroy() to unbind.

const handle = storefront.search.bindTo(document.querySelector('#search-input'), {
  minChars: 3,   // default: 3
  debounce: 150, // ms, default: 150
})

handle.on('results', (evt, results, query) => {
  renderResults(results.products, results.groups)
})

handle.on('cleared', () => clearResults())
handle.on('error', (evt, err) => console.error('Search failed', err))

// later, e.g. on component unmount:
handle.destroy()

You don’t need to manage the connection yourself — bindTo reuses one socket per storefront instance and lets it disconnect on inactivity automatically.

search(query, opts?, fn) — build your own UI

Lower-level: runs a single query without binding to an input. Auto-connects if needed.

storefront.search.search('running shoes', { minChars: 3 }, (err, results) => {
  if (err) return console.error(err)
  console.log(results.products, results.groups)
})

connect(fn?) — pre-warm the connection

Rarely needed directly — both search() and bindTo() connect automatically. Useful if you want to open the connection earlier than the first query (e.g. as soon as a search UI becomes visible, before the user has typed anything) to shave connection latency off the first result.

storefront.search.connect((err, socket) => {
  if (err) return console.error('Could not connect to search', err)
})

Results shape

{
  products: [
    { id, name, slug, url, type, vendor, price, image, vars, skus }
  ],
  groups: [
    { type, name, slug } // Collection, ProductType, Vendor, ProductSet, or Tag matches
  ]
}

Which fields are present depends on config passed to createStorefront() — image and price require withImages/withPrices (both on by default for headless).

Was this page helpful?