npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

medusa-plugin-elasticsearch

v1.1.2

Published

Elasticsearch search plugin for Medusa v2

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/sync with authentication
  • Admin UI settings page at /settings/elasticsearch with 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


Installation

1. Install the plugin in your Medusa project:

npm install medusa-plugin-elasticsearch

2. Set environment variables in .env:

ELASTIC_CLOUD_ID=your_cloud_id
ELASTIC_USER_NAME=your_username
ELASTIC_PASSWORD=your_password

Or for a local Elasticsearch 9.x instance:

ELASTIC_NODE=http://localhost:9200

3. 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/search

Search products indexed in Elasticsearch. No authentication required.

Request body:

{
  "q": "sweatshirt",
  "offset": 0,
  "limit": 20,
  "filter": {}
}

Store: Search Categories

POST /store/categories/search

Search 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/sync

Triggers 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 product
  • product.updated -- re-indexes the updated product
  • product.deleted -- removes the product from the index

Categories:

  • product-category.created -- indexes the new category
  • product-category.updated -- re-indexes the updated category
  • product-category.deleted -- removes the category from the index

Indexing Rules

  • Products: Only published products are indexed. Draft/unpublished products are removed from the index.
  • Categories: Only is_active: true and is_internal: false categories 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 exports

Roadmap

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