How to Add MeiliSearch to Your Medusa 2.0 Project

Enhance your Medusa 2.0 project with powerful search functionality using MeiliSearch. This guide walks you through setting up a MeiliSearch instance, configuring the backend with a community plugin, and integrating it into your storefront for seamless product search.

How to Add MeiliSearch to Your Medusa 2.0 Project
MeiliSearch integration with MedusaJS hosted on Railway

This guide assumes you already have a working Medusa 2.0 project with both a backend and storefront set up. We'll walk you through implementing MeiliSearch step by step. If you'd rather skip the manual setup, you can use my Railway template for an effortless deployment where everything is preconfigured:

Deploy on Railway (OBS: requires hobby plan 5$/m)
Railway caps free users to 5 services. The template above has 7. I also have a TRIAL version that does not include MeiliSearch.

If you're ready to dive into the implementation yourself, or if you just want to understand how it is implemented in the template above, this guide will cover it all, in three simple steps:

  1. Run a MeiliSearch instance
  2. Implement MeiliSearch in the Medusa backend
  3. Configure MeiliSearch in the storefront

1. Run a MeiliSearch Instance

MeiliSearch is a powerful, open-source search engine that runs as a separate service, not just an npm dependency. Below are three methods to run it: RailwayDocker, or MeiliSearch Cloud. We'll cover the first two in detail.

Method 1: Railway

  1. Go to your project canvas on Railway and create a new service.
  2. Choose the Docker Image option and search for getmeili/meilisearch:latest.
  1. Once deployed:
    • Rename the service to MeiliSearch.
    • Navigate to the Variables tab and add the following environment variables:
MEILI_DB_PATH="/meili_data/data.ms"
MEILI_ENV="production"
MEILI_HTTP_ADDR=":::3331"
MEILI_MASTER_KEY="some-long-random-string"
MEILI_PUBLIC_URL="${{RAILWAY_PUBLIC_DOMAIN}}"
PORT="3331"
  1. Attach storage volume, right click on the MeiliSearch container and choose Attach Volume from the menu
  1. Set mount path to /meili_data
  1. Enable networking, go to Setting -> Networking and turn on HTTP

Thatā€™s it! Your MeiliSearch instance is now ready. You can skip the Docker section now that youā€™ve used Railway instead. Use image below for reference your finished result.

Method 2: Docker (Local or VPS)

Use the following Docker Compose configuration to set up MeiliSearch locally or on a VPS:

docker-compose.yml

services:
  meilisearch:
    image: getmeili/meilisearch:latest
    ports:
      - "7700:7700"
    volumes:
      - ~/data.ms:/meili_data/data.ms
    environment:
      MEILI_MASTER_KEY: your_master_key_here
    healthcheck:
      test: ["CMD-SHELL", "wget --spider http://localhost:7700 || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 5

If you are not familiar with Docker compose, you may refer to the quick start guide. Additionally, more information about local instance of MeiliSearch can be found here.

Method 3: MeiliSearch Cloud

If you prefer a managed solution or want to support the developers, consider using MeiliSearch Cloud. It provides seamless deployment and additional features like facet search, semantic search, and geosearch.

Meilisearch | Cloud
Meilisearch: A powerful, open-source search engine offering fast and relevant full-text searches. Enhance your search capabilities with features like facet search, semantic search, hybrid search, and geosearch. Optimize indexing with best practices and enjoy seamless deployment with Meilisearch Cloud for an improved search experience.

2. Backend Implementation

Since the Medusa core team hasnā€™t released an official MeiliSearch module for version 2.0 yet, weā€™ll use the community-created @rokmohar/medusa-plugin-meilisearch. Make sure to ā­ their repository!

Step 1: Install the Plugin

Run one of these commands in your Medusa backend directory:

npm install --save @rokmohar/medusa-plugin-meilisearch
# or
yarn add @rokmohar/medusa-plugin-meilisearch

Step 2: Configure Environment Variables

Add these variables to your backendā€™s .env file:

MEILISEARCH_HOST=http://localhost:7700 # Or public cloud URL if you are using cloud an online MeiliSearch instance (Railway)
MEILISEARCH_API_KEY=your_master_key_here

And, if you are also hosting your project on Railway add the following two variables to your Medusa 2.0 backend Railway service:

MEILISEARCH_API_KEY="${{MeiliSearch.MEILI_MASTER_KEY}}"
MEILISEARCH_HOST="https://${{MeiliSearch.MEILI_PUBLIC_URL}}"

Step 3: Update Medusa Configuration

In your medusa-config.js, add the plugin configuration:

modules: [
  // ... all your other modules,
  {
    resolve: '@rokmohar/medusa-plugin-meilisearch',
    options: {
      config: {
        host: process.env.MEILISEARCH_HOST,
        apiKey: process.env.MEILISEARCH_API_KEY
      },
      settings: {
        products: {
          indexSettings: {
            searchableAttributes: ['title', 'description', 'variant_sku'],
            displayedAttributes: ['title', 'description', 'variant_sku', 'thumbnail', 'handle']
          },
          primaryKey: 'id'
        }
      }
    }
  }
]
// rest of your config...

See how I added it: https://github.com/rpuls/medusajs-2.0-for-railway-boilerplate/blob/master/backend/medusa-config.js

Step 4: Create Product Indexing Subscribers

We are going to be indexing products during 3 events; creation, updating, deletion. This can be done by creating two simple subscribers. All credits for these two subscribers goes to https://github.com/rokmohar. Create two files in your backend's /subscribers directory:

src/subscribers/product-upsert.ts (create and update)

import type { SubscriberArgs, SubscriberConfig } from '@medusajs/framework'
import { IProductModuleService } from '@medusajs/types'
import { Modules } from '@medusajs/utils'
import { ProductEvents, SearchUtils } from '@medusajs/utils'
import { MeiliSearchService } from '@rokmohar/medusa-plugin-meilisearch'

export default async function productUpsertHandler({ event: { data }, container }: SubscriberArgs<{ id: string }>) {
  const productId = data.id

  const productModuleService: IProductModuleService = container.resolve(Modules.PRODUCT)
  const meiliSearchService: MeiliSearchService = container.resolve('@rokmohar/medusa-plugin-meilisearch')

  const product = await productModuleService.retrieveProduct(productId)
  await meiliSearchService.addDocuments('products', [product], SearchUtils.indexTypes.PRODUCTS)
}

export const config: SubscriberConfig = {
  event: [ProductEvents.PRODUCT_CREATED, ProductEvents.PRODUCT_UPDATED]
}

src/subscribers/product-delete.ts

import type { SubscriberArgs, SubscriberConfig } from '@medusajs/framework'
import { ProductEvents } from '@medusajs/utils'
import { MeiliSearchService } from '@rokmohar/medusa-plugin-meilisearch'

export default async function productDeleteHandler({ event: { data }, container }: SubscriberArgs<{ id: string }>) {
    const productId = data.id

    const meiliSearchService: MeiliSearchService = container.resolve('@rokmohar/medusa-plugin-meilisearch')
    await meiliSearchService.deleteDocument('products', productId)
}

export const config: SubscriberConfig = {
    event: ProductEvents.PRODUCT_DELETED
}

That's all for the backend! šŸš€


3. Storefront Implementation

The storefront implementation is straightforward if you're using Medusa's official Next.js starter templateā€”it already includes MeiliSearch integration! If not, replicate their implementation by referencing /search-client.ts from the starter.

Step 1: Configure Environment Variables

Add these variables to .env.local in your storefront:

# MeiliSearch Configuration
NEXT_PUBLIC_SEARCH_ENDPOINT=http://localhost:7700 # Or public cloud URL if you are using an online meiliSearch instance (Railway)
NEXT_PUBLIC_SEARCH_API_KEY=your_master_key_here
NEXT_PUBLIC_INDEX_NAME=products

And, if you are also hosting your project on Railway add the following two variables to your Storefront Railway service:

NEXT_PUBLIC_SEARCH_API_KEY="${{MeiliSearch.MEILI_MASTER_KEY}}"
NEXT_PUBLIC_SEARCH_ENDPOINT="https://${{MeiliSearch.MEILI_PUBLIC_URL}}"
NEXT_PUBLIC_INDEX_NAME="products"
Note: while the masker key works for searching, it is not recommended to use in your storefront, as it should be kept secret. Once you have tested that the integration works, I recommend that you generate a search key and use that in your storefront instead.

All done!

You should now have a fully functional MeiliSearch integration with your Medusa project! Test it out by using the search field in your storefront šŸš€