@elasticpath/catalog-search-instantsearch-adapter
v1.0.0
Published
An adapter that enables you to use the [InstantSearch.js](https://github.com/algolia/instantsearch) library with [Elastic Path's Catalog Search API](https://elasticpath.com).
Keywords
Readme
@elasticpath/catalog-search-instantsearch-adapter
An adapter that enables you to use the InstantSearch.js library with Elastic Path's Catalog Search API.
Elastic Path's Catalog Search API provides a powerful search and discovery experience for your commerce catalog. This adapter translates InstantSearch.js requests into Catalog Search API requests and transforms the responses back into InstantSearch format.
Features
- 🔄 Seamless integration with InstantSearch.js widgets
- 🔍 Full-text search with highlighting
- 🎛️ Faceted search and filtering
- 📄 Pagination and result sorting
- ⚡ Support for both vanilla JS and React InstantSearch
- 🎯 Union search support for unified results
Demo
Try out the repo to see the adapter in action.
Quick Start
Installation
npm install @elasticpath/catalog-search-instantsearch-adapteror
yarn add @elasticpath/catalog-search-instantsearch-adapteror
pnpm add @elasticpath/catalog-search-instantsearch-adapterYou'll also need to install the Elastic Path SDK and InstantSearch.js:
npm install @epcc-sdk/sdks-shopper instantsearch.js
# or for React
npm install @epcc-sdk/sdks-shopper react-instantsearch instantsearch.jsBasic Usage
import instantsearch from 'instantsearch.js';
import CatalogSearchInstantSearchAdapter from '@elasticpath/catalog-search-instantsearch-adapter';
import { configureClient, client } from '@epcc-sdk/sdks-shopper';
// Configure your Elastic Path shopper client with built-in auth
configureClient(
{
baseUrl: 'https://euwest.api.elasticpath.com',
},
{
clientId: 'YOUR_CLIENT_ID',
storage: 'localStorage', // Optional: persist tokens
}
);
// Create the adapter
const catalogSearchAdapter = new CatalogSearchInstantSearchAdapter({
client: client,
});
// Initialize InstantSearch with the adapter
const search = instantsearch({
indexName: 'search',
searchClient: catalogSearchAdapter.searchClient,
});
// Add widgets
search.addWidgets([
instantsearch.widgets.searchBox({
container: '#searchbox',
}),
instantsearch.widgets.hits({
container: '#hits',
}),
instantsearch.widgets.refinementList({
container: '#brand-list',
attribute: 'extensions.products(product_brands).name',
}),
]);
search.start();With React
import React from 'react';
import {
InstantSearch,
SearchBox,
Hits,
RefinementList,
Pagination,
} from 'react-instantsearch';
import CatalogSearchInstantSearchAdapter from '@elasticpath/catalog-search-instantsearch-adapter';
import { configureClient, client } from '@epcc-sdk/sdks-shopper';
// Configure authentication (typically done once in your app initialization)
configureClient(
{
baseUrl: 'https://euwest.api.elasticpath.com',
},
{
clientId: 'YOUR_CLIENT_ID',
storage: 'localStorage',
}
);
// Create the adapter
const catalogSearchAdapter = new CatalogSearchInstantSearchAdapter({
client: client,
});
function Search() {
return (
<InstantSearch
indexName="search"
searchClient={catalogSearchAdapter.searchClient}
>
<SearchBox />
<div className="search-panel">
<div className="search-panel__filters">
<RefinementList attribute="extensions.products(product_brands).name" />
</div>
<div className="search-panel__results">
<Hits />
<Pagination />
</div>
</div>
</InstantSearch>
);
}Index Name Configuration
The indexName parameter in InstantSearch configuration determines which Elastic Path search endpoint to use:
indexName: 'search'- For regular product search (required)indexName: 'autocomplete'- For autocomplete/suggestions (when using with autocomplete.js)
These values map directly to the type parameter in Elastic Path's Catalog Search API.
Configuration Options
Constructor Parameters
const adapter = new CatalogSearchInstantSearchAdapter({
// Required: Pre-configured Elastic Path shopper SDK client
client: shopperClient,
});Including Related Resources (include)
EP's catalog-search response only contains hits and relationships.<resource>.data.id references by default — full resource data (image URLs, file metadata, component products) is not inlined. Set include to forward the EP ?include= URL query parameter; the adapter resolves the returned included block against each hit and inlines the full records onto the hit.
const adapter = new CatalogSearchInstantSearchAdapter({
client,
additionalSearchParameters: { query_by: "name,description" },
include: ["main_image"], // also accepts "files", "component_products"
});
// In a Hits widget:
const Hit = ({ hit }) => (
<article>
{hit.main_image && <img src={hit.main_image.link.href} alt={hit.name} />}
<h3>{hit.name}</h3>
</article>
);Cardinality follows the EP relationship shape:
| include value | Resolved field on hit | Type |
| -------------------- | --------------------- | ----------------------------- |
| "main_image" | hit.main_image | single record or undefined |
| "files" | hit.files | array (in relationships order) |
| "component_products" | hit.component_products | array (in relationships order) |
Missing references (a relationship pointing at an id not present in included) are skipped silently — the field is left absent rather than set to a partial record. If the server omits the included block entirely while include was requested, the adapter logs a single console.warn per instance to surface the server-side mismatch.
Behaviour is fully back-compat: omit include (or pass []) and the adapter neither sends a query field nor merges anything onto hits — they retain their original relationships.<resource>.data shape.
Widget Compatibility
The adapter supports most InstantSearch widgets out of the box:
✅ Supported Widgets
Search
searchBox- Basic search inputautocomplete- Search with suggestions (use withindexName: 'autocomplete')voiceSearch- Voice input support
Results
hits- Display search resultsinfiniteHits- Infinite scrolling resultshighlight- Highlight matching termssnippet- Show text snippets
Filtering
refinementList- Filter by facet valueshierarchicalMenu- Category navigationrangeSlider/rangeInput- Numeric range filterstoggleRefinement- Boolean filtersclearRefinements- Clear all filterscurrentRefinements- Show active filters
Pagination
pagination- Page navigationhitsPerPage- Results per page selector
Metadata
stats- Search statisticsbreadcrumb- Navigation breadcrumb
⚠️ Widgets with Limitations
geoSearch- Not supported (Elastic Path doesn't support geo-search)queryRuleCustomData- Limited supportsortBy- Useconfigurewidget withadditionalSearchParameters.sort_by
Widget Usage Examples
Hierarchical Categories
instantsearch.widgets.hierarchicalMenu({
container: '#categories',
attributes: [
'categories.lvl0',
'categories.lvl1',
'categories.lvl2',
],
});Price Range Filter
instantsearch.widgets.rangeSlider({
container: '#price-range',
attribute: 'price.USD.float_price',
});Custom Refinement List
instantsearch.widgets.refinementList({
container: '#brands',
attribute: 'extensions.products(product_brands).name',
limit: 10,
showMore: true,
showMoreLimit: 50,
searchable: true,
searchablePlaceholder: 'Search brands',
});Union Search
The adapter supports union search to query multiple collections simultaneously:
const adapter = new CatalogSearchInstantSearchAdapter({
client: client,
union: true,
});Compatibility
| Elastic Path Catalog Search | InstantSearch.js | Adapter Version | |---------------------------|------------------|-----------------| | v1.x | v4.x | 0.0.x | | v1.x | v3.x | 0.0.x |
Migration from Algolia
If you're migrating from Algolia to Elastic Path, most of your InstantSearch implementation will work without changes. Key differences:
- Replace the Algolia search client with this adapter
- Update attribute names to match your Elastic Path catalog structure
- Configure authentication using Elastic Path SDK instead of Algolia API keys
Examples
Check out our examples directory for complete implementations:
- React InstantSearch Example - Full-featured search UI with React
- Vanilla JS Example - Pure JavaScript implementation
Support
License
MIT
