Algolia
Sync your product catalog to Algolia for storefront search
medusa-plugin-algolia-plus
A Medusa v2 plugin that syncs your product catalog to Algolia and powers storefront search through the Medusa backend — so your Algolia write key never reaches the browser.
- Zero application code: register the plugin, set three env vars, and products auto-sync
- Ships a drop-in InstantSearch Copy to clipboard
searchClientfor the storefront - Idempotent by design — Copy to clipboard
objectID = product.id, so the index can be rebuilt at any time
Features
- Automatic sync — product, variant, category, collection, and order events keep the index current
- Key-safe storefront search — a Copy to clipboard
/store/products/searchroute proxies to Algolia; the admin key stays on the backend - Full reindex — one-click catalog rebuild from the admin; running it twice yields no duplicates
- Stock-accurate availability — derived from inventory levels and refreshed on order events
- Configurable records — static field overrides, metafield mapping, or a full custom mapper
- Index settings out of the box — searchable attributes, faceting, and ranking applied on every reindex
- Production hardening — retry with backoff + jitter, structured logging, never-throw subscribers, workflow compensation
- Admin UI — Settings → Algolia: connection health, reindex, and per-product re-sync
Requirements
- Medusa v2 (Copy to clipboard
@medusajs/medusa 2.13.5) - Node.js Copy to clipboard
>=20 - An Algolia account with a write/admin API key
Installation
12npm install medusa-plugin-algolia-plus# or: pnpm add medusa-plugin-algolia-plus / yarn add medusa-plugin-algolia-plus
The module declares no database models — there is no migration to run.
Setup
Add the following environment variables:
1234567ALGOLIA_APP_ID=your_app_idALGOLIA_API_KEY=your_write_api_keyALGOLIA_PRODUCT_INDEX_NAME=products# OptionalALGOLIA_BATCH_SIZE=50 # reindex page size, 1–1000ALGOLIA_STOREFRONT_URL=https://shop.example.com # builds each record's `url`
Use a write key, not a Search-Only key. Indexing and settings calls need the Copy to clipboardaddObject, Copy to clipboarddeleteObject, and Copy to clipboardeditSettingsACLs. A Search-Only key fails with "Not enough rights to add an object".
Then register the plugin in Copy to clipboardmedusa-config.js:
123456789101112131415const { ALGOLIA_APP_ID, ALGOLIA_API_KEY, ALGOLIA_PRODUCT_INDEX_NAME } = process.envmodule.exports = defineConfig({// ...plugins: [{resolve: "medusa-plugin-algolia-plus",options: {appId: ALGOLIA_APP_ID,apiKey: ALGOLIA_API_KEY, // write key — stays on the backendproductIndexName: ALGOLIA_PRODUCT_INDEX_NAME,},},],})
Invalid options throw at Medusa startup (Copy to clipboard[algolia] Invalid module options: …), never silently at the first sync.
Configuration
Option Type Default Description Copy to clipboardappId Copy to clipboardstring — Algolia Application ID (required) Copy to clipboardapiKey Copy to clipboardstring — Algolia write key (required) Copy to clipboardproductIndexName Copy to clipboardstring — Product index name (required) Copy to clipboardbatchSize Copy to clipboardnumber (1–1000) Copy to clipboard50 Full-reindex / fan-out page size Copy to clipboardstorefrontUrl Copy to clipboardstring (URL) — Builds each record's Copy to clipboardurl Copy to clipboardcurrencyCodes Copy to clipboardstring[] all Restrict indexed currencies Copy to clipboardproductMapper Copy to clipboardfunction default Replace the record shape entirely Copy to clipboardfieldOverrides Copy to clipboardRecord<string, unknown> — Static fields on every record Copy to clipboardmetadataFields Copy to clipboardRecord<string, string> — Map Copy to clipboardproduct.metadata keys onto record fields Copy to clipboardsearchableAttributes Copy to clipboardstring[] see below Applied at reindex time Copy to clipboardattributesForFaceting Copy to clipboardstring[] see below Applied at reindex time Copy to clipboardcustomRanking Copy to clipboardstring[] Copy to clipboarddesc(availability) Applied at reindex time Copy to clipboardrequestTimeoutMs Copy to clipboardnumber (1k–120k) Copy to clipboard30000 Per-request HTTP timeout Copy to clipboardmaxRetries Copy to clipboardnumber (0–10) Copy to clipboard3 Retries on transient Algolia errors Copy to clipboardretryBaseDelayMs Copy to clipboardnumber (0–60k) Copy to clipboard200 Backoff base delay
Default index settings, applied automatically at the start of every full reindex:
Setting Default Copy to clipboardsearchableAttributes Copy to clipboardtitle, Copy to clipboarddescription, Copy to clipboardvariants.sku, Copy to clipboardtags, Copy to clipboardcategories.name, Copy to clipboardcollection.title Copy to clipboardattributesForFaceting Copy to clipboardsearchable(categories.handle), Copy to clipboardcollection.handle, Copy to clipboardtags, Copy to clipboardavailability Copy to clipboardcustomRanking Copy to clipboarddesc(availability) (in-stock first)
For numeric price filtering, add per-currency facets to Copy to clipboardattributesForFaceting, e.g. Copy to clipboardfilterOnly(price_min.eur).
How It Works
Automatic sync
Thin, never-throwing subscribers keep the index current — a failed sync is logged and can never break a product save:
You do in Medusa In Algolia Create / edit a product Record upserted (or removed if unpublished) Delete a product Record removed Add / edit a variant, change a price Parent product reindexed Rename a category / collection Products in it reindexed Place / cancel / return an order Those products' Copy to clipboardavailability refreshed
A product is in search only while Copy to clipboardstatus === "published"; unpublishing removes it and republishing re-adds it under the same id.
Full reindex
Copy to clipboardPOST /admin/algolia/sync returns Copy to clipboard202 immediately and walks the whole catalog page by page (Copy to clipboardbatchSize), applying index settings first. Trigger it from Settings → Algolia → Reindex all products. It is the catch-all repair for any staleness, and is safe to run at any time.
Availability
Medusa 2.13.5 emits no inventory-level event, so availability is tracked through order events: placing an order reserves stock, cancel/return releases it. A variant counts as available when it is not inventory-managed, allows backorder, or has Copy to clipboardstocked − reserved ≥ required.
Two changes can't be caught in real time and are repaired by a full reindex: a direct admin stock-level edit, and deleting a category, collection, or single variant.
The Algolia record
The default mapper produces one record per product:
12345678910111213141516{"objectID": "prod_123", // always the product id"title": "Cool Shirt","handle": "cool-shirt","description": "A very cool shirt","thumbnail": "https://cdn/thumb.jpg","images": [{ "id": "img_1", "url": "https://cdn/1.jpg" }],"categories": [{ "id": "cat_1", "name": "Shirts", "handle": "shirts" }],"collection": { "id": "col_1", "title": "Spring", "handle": "spring" },"tags": ["summer"],"variants": [{ "id": "var_1", "sku": "SHIRT-S", "prices": { "eur": 1999 } }],"price_min": { "eur": 1999 }, // per currency, across variants"price_max": { "eur": 2999 },"availability": true,"url": "https://shop.example.com/products/cool-shirt"}
Prices are keyed by lowercase currency code, in minor units.
Customizing it
12345678910111213// 1. Static fields on every recordfieldOverrides: { brand: "Acme" }// 2. Project product.metadata onto record fieldsmetadataFields: { material: "material", season: "season_facet" }// 3. Replace the shape entirelyimport type { ProductMapper } from "medusa-plugin-algolia-plus"const productMapper: ProductMapper = (product) => ({objectID: product.id, // must equal the product idname: product.title,})
Storefront Integration
The package ships an InstantSearch Copy to clipboardsearchClient that points at the Medusa route, so no Algolia key is needed in the browser:
123456import { createAlgoliaSearchClient } from "medusa-plugin-algolia-plus/storefront/create-search-client"export const searchClient = createAlgoliaSearchClient({backendUrl: process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL!,publishableKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY!,})
123456<InstantSearch searchClient={searchClient} indexName="products"><SearchBox /><RefinementList attribute="categories.handle" /><Hits /><Pagination /></InstantSearch>
The full InstantSearch widget ecosystem works unchanged. Pass a custom Copy to clipboardfetchFn for SSR or extra headers.
API Endpoints
Method Route Description Copy to clipboardPOST Copy to clipboard/admin/algolia/sync Trigger a full reindex (returns Copy to clipboard202) Copy to clipboardGET Copy to clipboard/admin/algolia/sync Connection health, index name, current options Copy to clipboardPOST Copy to clipboard/admin/algolia/products/:id Re-sync a single product synchronously Copy to clipboardPOST Copy to clipboard/store/products/search Search; returns the raw Algolia Copy to clipboardSearchResponse
1234curl -X POST http://localhost:9000/store/products/search \-H "content-type: application/json" \-H "x-publishable-api-key: pk_..." \-d '{"query":"shirt","hitsPerPage":12}'
Body fields: Copy to clipboardquery, Copy to clipboardpage, Copy to clipboardhitsPerPage (1–200), Copy to clipboardfilters, Copy to clipboardfacetFilters, Copy to clipboardnumericFilters, Copy to clipboardfacets, Copy to clipboardattributesToRetrieve. Invalid bodies fail with a structured Copy to clipboard400.
Exports
123456789101112import {algoliaModule,ALGOLIA_MODULE,AlgoliaModuleService,syncProductsWorkflow,deleteProductsFromAlgoliaWorkflow,productToAlgoliaRecord,resolveAlgoliaOptions,AlgoliaEvents,} from "medusa-plugin-algolia-plus"import type { ProductMapper, AlgoliaOptions } from "medusa-plugin-algolia-plus"
Copy to clipboardAlgoliaModuleService exposes Copy to clipboardindexData, Copy to clipboardretrieveFromIndex, Copy to clipboarddeleteFromIndex, Copy to clipboardsearch, Copy to clipboardconfigureIndexSettings, Copy to clipboardhealthCheck, and Copy to clipboardmapProducts.
Troubleshooting
Symptom Cause / fix Copy to clipboard[algolia] Invalid module options at startup A required env var is missing — intended fail-fast Copy to clipboardNot enough rights to add an object You used a Search-Only key — switch to a write key "Index products does not exist" The index is created on the first write — fix the key, then reindex No "Algolia" in Settings, or routes 404 Plugin not built, or env not set, so it isn't registered Store search returns Copy to clipboard503 Algolia module not registered (missing env)
Development
1234pnpm installpnpm typecheck # tsc --noEmit (module + admin)pnpm test # 79 unit tests across 9 suitespnpm build # medusa plugin:build → .medusa/server
License
MIT

