medusa-plugin-elasticsearch
v1.1.2
Published
Elasticsearch search plugin for Medusa v2
Maintainers
Readme
Medusa Elasticsearch Plugin
Elasticsearch search plugin for Medusa v2. Provides automatic product and category indexing, full-text search, and admin sync capabilities powered by Elasticsearch.
Features
- Product and category indexing with real-time sync on create, update, and delete events
- Full reindex via admin API endpoint or scheduled job (daily at midnight)
- Batch syncing with pagination (50 items per batch) for large catalogs
- Smart filtering -- only published products and active/non-internal categories are indexed
- Custom document transformers per index type
- Configurable Elasticsearch mappings, analyzers, and tokenizers
- Search API for products (
POST /store/products/search) and categories (POST /store/categories/search) - Admin API at
POST /admin/elasticsearch/syncwith authentication - Admin UI settings page at
/settings/elasticsearchwith sync button and search testing - Workflow compensation -- automatic rollback on indexing failures
- Built as a Medusa v2 module with workflows, subscribers, and scheduled jobs
Prerequisites
- Medusa v2 application (v2.5.0+)
- Elasticsearch 9.x instance or Elastic Cloud
Installation
1. Install the plugin in your Medusa project:
npm install medusa-plugin-elasticsearch2. Set environment variables in .env:
ELASTIC_CLOUD_ID=your_cloud_id
ELASTIC_USER_NAME=your_username
ELASTIC_PASSWORD=your_passwordOr for a local Elasticsearch 9.x instance:
ELASTIC_NODE=http://localhost:92003. Register the plugin and module in medusa-config.ts:
import { defineConfig } from "@medusajs/framework/utils"
export default defineConfig({
// Register the plugin (loads subscribers, API routes, workflows, jobs)
plugins: [
{
resolve: "medusa-plugin-elasticsearch",
options: {},
},
],
// Register the Elasticsearch module
modules: [
{
resolve: "medusa-plugin-elasticsearch/modules/elasticsearch",
options: {
config: {
// Option A: Elastic Cloud
cloud: {
id: process.env.ELASTIC_CLOUD_ID,
},
auth: {
username: process.env.ELASTIC_USER_NAME,
password: process.env.ELASTIC_PASSWORD,
},
// Option B: Local instance
// node: process.env.ELASTIC_NODE,
},
settings: {
products: {
// Optional: custom Elasticsearch mappings
mappings: {
properties: {
id: { type: "keyword" },
title: { type: "text" },
description: { type: "text" },
handle: { type: "keyword" },
},
},
// Optional: custom index settings (analyzers, tokenizers)
settings: {
analysis: {
tokenizer: {
autocomplete: {
type: "edge_ngram",
min_gram: 2,
max_gram: 10,
token_chars: ["letter", "digit"],
},
},
analyzer: {
autocomplete_index: {
type: "custom",
tokenizer: "autocomplete",
filter: ["lowercase"],
},
},
},
},
},
categories: {
mappings: {
properties: {
id: { type: "keyword" },
name: { type: "text" },
handle: { type: "keyword" },
description: { type: "text" },
},
},
},
},
},
},
],
})Options
Module Options
| Name | Description | Required |
|------|-------------|----------|
| config | Elasticsearch client configuration (cloud, auth, node, etc.) | true |
| settings | Index configurations keyed by index name (products, categories, or custom) | false |
Index Options (per index in settings)
| Name | Description | Required |
|------|-------------|----------|
| transformer | Custom document transformer function | false |
| mappings | Elasticsearch mapping configuration | false |
| settings | Elasticsearch index settings (analyzers, tokenizers, normalizers) | false |
API Endpoints
Store: Search Products
POST /store/products/searchSearch products indexed in Elasticsearch. No authentication required.
Request body:
{
"q": "sweatshirt",
"offset": 0,
"limit": 20,
"filter": {}
}Store: Search Categories
POST /store/categories/searchSearch product categories. No authentication required.
Request body:
{
"q": "electronics",
"offset": 0,
"limit": 20
}Response: Standard Elasticsearch search response with hits.
Admin: Trigger Full Sync
POST /admin/elasticsearch/syncTriggers a full reindex of all products and categories. Requires admin authentication (session, bearer, or API key).
Response:
{
"message": "Syncing products to Elasticsearch"
}The sync runs asynchronously, processing items in batches of 50.
Automatic Syncing
Real-time Events
The plugin automatically syncs on these events:
Products:
product.created-- indexes the new productproduct.updated-- re-indexes the updated productproduct.deleted-- removes the product from the index
Categories:
product-category.created-- indexes the new categoryproduct-category.updated-- re-indexes the updated categoryproduct-category.deleted-- removes the category from the index
Indexing Rules
- Products: Only
publishedproducts are indexed. Draft/unpublished products are removed from the index. - Categories: Only
is_active: trueandis_internal: falsecategories are indexed. Inactive or internal categories are removed.
Scheduled Reindex
A scheduled job runs daily at midnight to trigger a full reindex via the elasticsearch.sync event.
Manual Reindex
Use the admin API endpoint or emit the event directly:
const eventBus = container.resolve("event_bus")
await eventBus.emit({ name: "elasticsearch.sync", data: {} })Workflows
The plugin exports workflows for direct use:
import {
syncProductsToElasticsearchWorkflow,
deleteProductsFromElasticsearchWorkflow,
syncCategoriesToElasticsearchWorkflow,
deleteCategoriesFromElasticsearchWorkflow,
} from "medusa-plugin-elasticsearch/workflows"
// Sync specific products
await syncProductsToElasticsearchWorkflow(container).run({
input: { ids: ["prod_01ABC"] },
})
// Sync specific categories
await syncCategoriesToElasticsearchWorkflow(container).run({
input: { ids: ["pcat_01ABC"] },
})
// Delete from index
await deleteProductsFromElasticsearchWorkflow(container).run({
input: { ids: ["prod_01ABC"] },
})
await deleteCategoriesFromElasticsearchWorkflow(container).run({
input: { ids: ["pcat_01ABC"] },
})Custom Transformer
By default, products are indexed with a built-in transformer that flattens variant data, tags, categories, and collections. Categories are indexed with parent/children metadata. You can override either:
{
settings: {
products: {
transformer: (product) => ({
id: product.id,
title: product.title,
description: product.description,
handle: product.handle,
thumbnail: product.thumbnail,
}),
},
categories: {
transformer: (category) => ({
id: category.id,
name: category.name,
handle: category.handle,
}),
},
},
}Architecture
src/
admin/ # Admin UI extension
lib/sdk.ts # Medusa JS SDK client
routes/settings/elasticsearch/
page.tsx # Settings page (sync + search testing)
modules/elasticsearch/ # Elasticsearch module
index.ts # Module definition (Module())
service.ts # ES client service
types.ts # Type definitions
loaders/initialize.ts # Index initialization on startup
subscribers/
product-upsert.ts # Sync on product.created / product.updated
product-deleted.ts # Remove on product.deleted
category-upsert.ts # Sync on product-category.created / updated
category-deleted.ts # Remove on product-category.deleted
elasticsearch-sync.ts # Full reindex (products + categories)
workflows/
sync-products-to-elasticsearch.ts # Fetch + index products
delete-products-from-elasticsearch.ts # Delete products from index
sync-categories-to-elasticsearch.ts # Fetch + index categories
delete-categories-from-elasticsearch.ts # Delete categories from index
index.ts # Workflow exports
api/
middlewares.ts # Validation + admin auth
store/products/search/ # POST /store/products/search
store/categories/search/ # POST /store/categories/search
admin/elasticsearch/sync/ # POST /admin/elasticsearch/sync
jobs/
elasticsearch-reindex.ts # Daily scheduled reindex
utils/
transformer.ts # Default product + category transformers
types/
index.ts # Public type exportsRoadmap
Planned features for future releases:
- [ ] i18n / multi-language support -- index per locale or field-suffix strategy for multilingual storefronts
- [x] ~~Admin UI extension -- dashboard widget with sync button, index status, and document counts~~
- [ ] Configurable reindex schedule -- allow custom cron expressions via module options
- [ ] Collection indexing -- sync product collections alongside products and categories
- [ ] Aggregation helpers -- pre-built faceted search support (price ranges, categories, tags)
- [ ] Geo-search support -- location-based product search leveraging Elasticsearch's geo queries
- [ ] Index aliasing -- zero-downtime reindexing using Elasticsearch index aliases
- [ ] Bulk reindex CLI command -- one-time full reindex via Medusa CLI
Additional Resources
Contributing
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
License
MIT
