• Сообщество
  • Связаться с нами
Документация
Плагины и интеграцииВсе расширения для Medusa от сообществаЭкспертыПодберите специалиста для разработки и развития вашего проекта на Medusa
КейсыПосмотрите примеры Medusa в продакшене и успешные внедрения
Меч Moscow
Комплексная e-commerce платформа на Medusa для московского fashion-бренда

Меч Moscow · Fashion

Нужна помощь в разработке плагина интеграции для Medusa?

Написать намНайти плагины

Gorgo снижает затраты на адаптацию Medusa к локальным рынкам.

Мы разрабатываем плагины интеграции, осуществляем поддержку и развиваем сообщество разработчиков на Medusa в Telegram.

  • Ресурсы Medusa
  • Плагины и интеграции
  • Эксперты
  • Кейсы
  • Medusa Чат в Telegram
  • Medusa Новости в Telegram
  • Документация Gorgo
  • Связаться с нами
  • head@gorgojs.com
  • TelegramGitHub
MedusaПлагиныMeiliSearch
MeiliSearch logo

MeiliSearch

Open-source поисковый движок для вашей витрины

Нужна доработка этого плагина?

Связаться с нами
npm install @rokmohar/medusa-plugin-meilisearch
Категория
Поиск
Создано
Rokmohar
Версия
1.4.3
Последнее обновление
2 дня назад
Ежемесячные загрузки
17416
Звезды на Github
61
npmNPMGitHubGithub
MedusaПлагиныMeiliSearch

MedusaJS v2 MeiliSearch Plugin with i18n Support

This plugin integrates MeiliSearch with your Medusa e-commerce store and adds support for internationalization (i18n) of your product catalog.

Features

  • Full-text search for your Medusa store
  • Real-time indexing
  • Typo-tolerance
  • Faceted search
  • Support for both products and categories
  • Internationalization (i18n) support with multiple strategies:
    1. Separate index per language
    2. Language-specific fields with suffix
  • Flexible translation configuration
  • Custom field transformations
  • Automatic field detection

Installation

Run the following command to install the plugin with npm:

npm install --save @rokmohar/medusa-plugin-meilisearch

Or with yarn:

yarn add @rokmohar/medusa-plugin-meilisearch

Upgrade to v1.0

This step is required only if you are upgrading from previous version to v1.0.

  • The plugin now supports new MedusaJS plugin system.
  • Subscribers are included in the plugin.
  • You don't need custom subscribers anymore, you can remove them.

Minimum Compatibility

Plugin versionMedusa version

Note: This plugin is only compatible with MedusaJS v2. For MedusaJS v1 / v2.3.x and older, use the legacy version.

Configuration

Add the plugin to your file:

import { loadEnv, defineConfig } from '@medusajs/framework/utils'
import { MeilisearchPluginOptions } from '@rokmohar/medusa-plugin-meilisearch'
loadEnv(process.env.NODE_ENV || 'development', process.cwd())
module.exports = defineConfig({
// ... other config
plugins: [
// ... other plugins
{
resolve: '@rokmohar/medusa-plugin-meilisearch',
options: {
config: {
host: process.env.MEILISEARCH_HOST ?? '',
apiKey: process.env.MEILISEARCH_API_KEY ?? '',
},
settings: {
// The key is used as the index name in Meilisearch
products: {
// Required: Index type
type: 'products',
// Optional: Whether the index is enabled. When disabled:
// - Index won't be created or updated
// - Documents won't be added or removed
// - Index won't be included in searches
// - All operations will be silently skipped
enabled: true,
// Optional: Specify which fields to include in the index
// If not specified, all fields will be included
fields: ['id', 'title', 'description', 'handle', 'variant_sku', 'thumbnail'],
indexSettings: {
searchableAttributes: ['title', 'description', 'variant_sku'],
displayedAttributes: ['id', 'handle', 'title', 'description', 'variant_sku', 'thumbnail'],
filterableAttributes: ['id', 'handle'],
},
primaryKey: 'id',
// Create your own transformer
/*transformer: (product) => ({
id: product.id,
// other attributes...
}),*/
},
categories: {
// Required: Index type
type: 'categories',
// Optional: Whether the index is enabled
enabled: true,
// Optional: Specify which fields to include in the index
// If not specified, all fields will be included
fields: ['id', 'name', 'description', 'handle', 'is_active', 'parent_id'],
indexSettings: {
searchableAttributes: ['name', 'description'],
displayedAttributes: ['id', 'name', 'description', 'handle', 'is_active', 'parent_id'],
filterableAttributes: ['id', 'handle', 'is_active', 'parent_id'],
},
primaryKey: 'id',
// Create your own transformer
/*transformer: (category) => ({
id: category.id,
name: category.name,
// other attributes...
}),*/
},
},
i18n: {
// Choose one of the following strategies:
// 1. Separate index per language
// strategy: 'separate-index',
// languages: ['en', 'fr', 'de'],
// defaultLanguage: 'en',
// 2. Language-specific fields with suffix
strategy: 'field-suffix',
languages: ['en', 'fr', 'de'],
defaultLanguage: 'en',
translatableFields: ['title', 'description'],
},
} satisfies MeilisearchPluginOptions,
},
],
})

⚠️ Worker Mode Considerations

Important: Product events and background tasks will not work if your Medusa instance is running in mode, because the server instance does not process subscribers or background jobs.

Depending on your setup:

  • Monolithic architecture (only one backend instance):
    Ensure you do not set the or environment variable. By default, Medusa will use mode, which supports both background processing and serving HTTP requests from the same instance.

  • Split architecture (separate server and worker instances):
    Follow the official Medusa documentation on worker mode.
    In this case, you must add this plugin in the worker instance, as the server instance does not handle event subscribers or background tasks.

i18n Configuration

The plugin supports two main strategies for handling translations, with flexible configuration options for each.

Basic Configuration

{
i18n: {
// Choose strategy: 'separate-index' or 'field-suffix'
strategy: 'field-suffix',
// List of supported languages
languages: ['en', 'fr', 'de'],
// Default language to fall back to
defaultLanguage: 'en',
// Optional: List of translatable fields
translatableFields: ['title', 'description', 'handle']
}
}

Advanced Field Configuration

You can provide detailed configuration for each translatable field:

{
i18n: {
strategy: 'field-suffix',
languages: ['en', 'fr', 'de'],
defaultLanguage: 'en',
translatableFields: [
// Simple field name
'title',
// Field with different target name
{
source: 'description',
target: 'content' // Will be indexed as content_en, content_fr, etc.
},
// Field with transformation
{
source: 'handle',
transform: (value) => value.toLowerCase().replace(/\s+/g, '-')
}
]
}
}

Custom Translation Transformer

The plugin provides a flexible way to transform your products with custom translations. Translations are passed directly to the default transformer via :

{
settings: {
products: {
type: 'products',
// ... other config
transformer: async (product, defaultTransformer, options) => {
const translations = {
title: [
{ language_code: 'en', value: 'Blue T-Shirt' },
{ language_code: 'fr', value: 'T-Shirt Bleu' },
],
description: [
{ language_code: 'en', value: 'A comfortable blue t-shirt' },
{ language_code: 'fr', value: 'Un t-shirt bleu confortable' },
],
}
return defaultTransformer(product, {
...options,
translations,
includeAllTranslations: true,
})
},
}
}
}

Pass to emit all language suffixes (e.g. , ). Without it only the current language suffix is written.

Integration with Medusa Native Translations

The recommended approach for production is to use the Medusa Translation module, which is built into Medusa v2.

1. Enable the translation feature flag and module in :

module.exports = defineConfig({
featureFlags: {
translation: true,
},
// ... other config
modules: [
// ... other modules
{
resolve: '@medusajs/medusa/translation',
},
],
})

2. Create a translations utility ():

import { ContainerRegistrationKeys } from '@medusajs/utils'
import type { MedusaContainer } from '@medusajs/framework'
import { TranslationMap } from '@rokmohar/medusa-plugin-meilisearch'
// Maps Medusa locale codes (e.g. 'sl-SI') to index field suffixes (e.g. 'sl')
export const LOCALE_MAP: Record<string, string> = {
'sl-SI': 'sl',
'en-US': 'en',
// add more as needed
}
export const getTranslations = async (
id: string,
langs: string[],
container: MedusaContainer,
): Promise<TranslationMap> => {
const query = container.resolve(ContainerRegistrationKeys.QUERY)
const { data: rows } = await query.graph({
entity: 'translation',
fields: ['reference_id', 'locale_code', 'translations'],
filters: { reference_id: id, locale_code: langs },
})
const result: TranslationMap = {}
for (const row of rows) {
const langCode = LOCALE_MAP[row.locale_code] ?? row.locale_code
for (const [field, value] of Object.entries(row.translations as Record<string, string>)) {
if (!result[field]) {
result[field] = []
}
result[field].push({ language_code: langCode, value })
}
}
return result
}

3. Use in your transformer:

The transformer receives — the real forwarded by workflow steps. It is when no container is available (e.g. during a manual index rebuild without a workflow context), so always guard with a fallback:

import { getTranslations, LOCALE_MAP } from './src/utils/translations'
{
settings: {
products: {
type: 'products',
indexSettings: {
searchableAttributes: ['title', 'title_sl', 'title_en'],
displayedAttributes: ['id', 'handle', 'title', 'title_sl', 'title_en', 'thumbnail'],
filterableAttributes: ['id'],
},
transformer: async (product, defaultTransformer, options) => {
if (!options?.container) {
// No container available — index without translations
return defaultTransformer(product, options)
}
const raw = await getTranslations(product.id, ['sl-SI', 'en-US'], options.container)
// Remap locale codes to match index field suffixes
const translations = Object.fromEntries(
Object.entries(raw).map(([field, values]) => [
field,
values.map((t) => ({
language_code: LOCALE_MAP[t.language_code] ?? t.language_code,
value: t.value,
})),
]),
)
return defaultTransformer(product, {
...options,
translations,
includeAllTranslations: true,
})
},
}
}
}

How reaches the transformer: Medusa plugin modules receive the awilix cradle proxy at construction time, not the real — so the service cannot self-supply it. Workflow steps (triggered by product/category events) have the real container from their context and forward it via . The transformer then resolves services (e.g. ) from it.

i18n Strategies

1. Separate Index per Language

This strategy creates a separate MeiliSearch index for each language. For example, if your base index is named "products", it will create:

  • products_en
  • products_fr
  • products_de

Benefits:

  • Better performance for language-specific searches
  • Language-specific settings and ranking rules
  • Cleaner index structure

2. Language-specific Fields with Suffix

This strategy adds language suffixes to translatable fields in the same index. For example:

  • title_en, title_fr, title_de
  • description_en, description_fr, description_de

Benefits:

  • Single index to maintain
  • Ability to search across all languages at once
  • Smaller storage requirements

Custom Translatable Fields

If no translatable fields are specified and using the field-suffix strategy, the plugin will automatically detect string fields as translatable. You can override this by explicitly specifying the fields:

{
i18n: {
strategy: 'field-suffix',
languages: ['en', 'fr'],
defaultLanguage: 'en',
// Only these fields will be translatable
translatableFields: ['title', 'description']
}
}

Product API Endpoints

Search for Product Hits

GET /store/meilisearch/products-hits

Query Parameters:

  • : Search query string
  • : (Optional) Limit results from search
  • : (Optional) Offset results from search
  • : (Optional) Language code
  • - Enable AI-powered semantic search (boolean)
  • - Semantic vs keyword search ratio (0-1)

Search for Products

GET /store/meilisearch/products

Query Parameters:

  • - MedusaJS fields expression
  • : (Optional) Limit results from search
  • : (Optional) Offset results from search
  • : (Optional, but required for ) Current region ID
  • : (Optional, but required for ) Current currency code
  • : Search query string
  • : (Optional) Language code
  • - Enable AI-powered semantic search (boolean)
  • - Semantic vs keyword search ratio (0-1)

Category Support

This plugin provides full support for MedusaJS v2 categories, including:

  • Real-time indexing of category changes
  • i18n support for category names and descriptions
  • Hierarchical category structure support with parent-child relationships
  • Custom category field transformations

Category Configuration Example

{
settings: {
categories: {
type: 'categories',
enabled: true,
fields: ['id', 'name', 'description', 'handle', 'is_active', 'parent_id'],
indexSettings: {
searchableAttributes: ['name', 'description'],
displayedAttributes: ['id', 'name', 'description', 'handle', 'is_active', 'parent_id'],
filterableAttributes: ['id', 'handle', 'is_active', 'parent_id'],
},
primaryKey: 'id',
},
},
i18n: {
strategy: 'field-suffix',
languages: ['en', 'fr', 'de'],
defaultLanguage: 'en',
translatableFields: ['name', 'description'], // Category-specific translatable fields
},
}

Category API Endpoints

Search for Category Hits

GET /store/meilisearch/categories-hits

Query Parameters:

  • : Search query string
  • : (Optional) Limit results from search
  • : (Optional) Offset results from search
  • : (Optional) Language code
  • - Enable AI-powered semantic search (boolean)
  • - Semantic vs keyword search ratio (0-1)

Search for Categories

GET /store/meilisearch/categories

Query Parameters:

  • - MedusaJS fields expression
  • : (Optional) Limit results from search
  • : (Optional) Offset results from search
  • : Search query string
  • : (Optional) Language code
  • - Enable AI-powered semantic search (boolean)
  • - Semantic vs keyword search ratio (0-1)

AI-Powered Semantic Search

This plugin supports AI-powered semantic search using vector embeddings. See docs/semantic-search.md for detailed configuration and usage instructions.

ENV variables

Add the environment variables to your and file:

# ... others vars
MEILISEARCH_HOST=
MEILISEARCH_API_KEY=

If you want to use with the from this README, use the following values:

# ... others vars
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_API_KEY=ms

docker-compose

You can add the following configuration for Meilisearch to your :

services:
# ... other services
meilisearch:
image: getmeili/meilisearch:latest
ports:
- '7700:7700'
volumes:
- ~/data.ms:/data.ms
environment:
- MEILI_MASTER_KEY=ms
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:7700']
interval: 10s
timeout: 5s
retries: 5

Add search to Medusa NextJS starter

You can find instructions on how to add search to a Medusa NextJS starter inside the nextjs folder.

FAQ

  • How do I include product categories and tags in my search index?
  • How do I include product variant prices (min_price, max_price) in my search index?
  • How do I include prices in the search response from the search endpoint?

Contributing

Feel free to open issues and pull requests!

Еще в этой категории

Посмотреть все
Поиск
MeiliSearch logo

MeiliSearch

От Vymalo

Подключите быстрый поиск с MeiliSearch

Загрузка данных
GitHubnpm
Поиск
Relewise logo

Relewise

От Relewise

Прокачайте поиск по товарам с Relewise

Загрузка данных
npm
Поиск
E

Elasticsearch

От Peterborodatyy

Elasticsearch search plugin for Medusa v2

Загрузка данных
GitHubnpm

Еще от этого автора

Посмотреть все
Платежи
Manual Payment logo

Manual Payment

От Rokmohar

Дефолтный провайдер для ручного приёма оплаты

Загрузка данных
GitHubnpm
Уведомления
Unsend logo

Unsend

От Rokmohar

Создавайте шаблоны писем с Unsend

Загрузка данных
GitHubnpm