September 23, 2025·Product
Improved Medusa Index
Oliver Juhl

Adrien de Peretti

Oliver Juhl & Adrien de Peretti
Unlock better cross-module filtering and faster queries with the new Medusa Index improvements.

We are excited to release improvements to Medusa Index, making all core entities indexable.
Medusa Index was built to improve the developer experience of filtering data across modules while improving the performance of data retrieval, especially for large data sets. In previous versions, only a select few entities were indexable, including Products, Variants, Prices, and Sales Channels.
In version 2.10.2, we expanded the indexable entities to include all entities in our core modules and entities from custom modules in your Medusa project. This improvement unlocks cross-module filtering for all data in your applications, provided the data is ingested into Medusa Index.
You can read more about the technical design of Medusa Index in our documentation.
Installing Medusa Index
Medusa Index is still in development, so it is not used by default in the core APIs and workflows. To use Index, you need to follow a few steps to install it in your Medusa project.
Install the Copy to clipboardnpm
package:
1yarn add @medusajs/index
Add Index as a module in Copy to clipboardmedusa-config.ts
:
123456789module.exports = defineConfig({// ...modules: [// ...{resolve: "@medusajs/index",},],})
Run the migrations to create the database tables related to Index:
1npx medusa db:migrate
Start the server:
1yarn dev
Indexing occurs when the server is started. As mentioned, we ingest Products, Variants, Prices, and Sales Channels by default, so the first indexation might take a while if your product catalog is large. You should see the following logs when the indexation has finished:
12345678910111213141516info: [Index engine] Checking for index changesinfo: [Index engine] Found 7 index changes that are either pending or processinginfo: [Index engine] syncing entity 'ProductVariant'info: [Index engine] syncing entity 'ProductVariant' done (+38.73ms)info: [Index engine] syncing entity 'Product'info: [Index engine] syncing entity 'Product' done (+18.21ms)info: [Index engine] syncing entity 'LinkProductVariantPriceSet'info: [Index engine] syncing entity 'LinkProductVariantPriceSet' done (+33.87ms)info: [Index engine] syncing entity 'Price'info: [Index engine] syncing entity 'Price' done (+22.79ms)info: [Index engine] syncing entity 'PriceSet'info: [Index engine] syncing entity 'PriceSet' done (+10.72ms)info: [Index engine] syncing entity 'LinkProductSalesChannel'info: [Index engine] syncing entity 'LinkProductSalesChannel' done (+11.45ms)info: [Index engine] syncing entity 'SalesChannel'info: [Index engine] syncing entity 'SalesChannel' done (+7.00ms)
After this, the installation of Index is completed.
You can now try Index by creating a custom API endpoint and querying your Products:
1234567891011121314151617import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"export async function GET(req: MedusaRequest, res: MedusaResponse) {const query = req.scope.resolve("query")const { data: products } = await query.index({entity: "product",fields: ["id", "sales_channels.*"],filters: {sales_channel: {id: "sc_12345",},},})res.json({ products })}
Note the use of Copy to clipboardquery.index
instead of Copy to clipboardquery.graph
.
Alternatively, you can enable the following feature flag and try it out in two core routes, Copy to clipboardGET /store/products
and Copy to clipboardGET /admin/products
:
123456789101112module.exports = defineConfig({// ...featureFlags: {index_engine: true},modules: [// ...{resolve: "@medusajs/index",},],})
In a later version, we will enable Index by default across a range of APIs.
Enabling cross-module filtering of core entities
It is a relatively common use case to want to filter core entities by an entity from a custom module. Let's demonstrate this with an example of filtering Orders from Medusa's core by a custom entity, Vendor.
This example assumes three things:
- A custom module with a Vendor data model exists in your Medusa project
- A link definition between Orders and Vendors exists
- Orders are linked to Vendors when they are created
Read more about managing links in our documentation.
To ingest data into Index, you need to use a new configuration Copy to clipboardfilterable
in the link definition, so the first step is to update the link definition we already had:
123456789101112131415import { defineLink } from "@medusajs/framework/utils";import OrderModule from "@medusajs/medusa/order";import VendorModule from "../modules/vendor";export default defineLink({linkable: OrderModule.linkable.order,isList: true,filterable: ["id", "display_id", "shipping_address"]},{linkable: VendorModule.linkable.vendor,filterable: ["id", "name"],});
The Copy to clipboardfilterable
option changes a regular link to an indexed link. In addition to this, Copy to clipboardfilterable
defines what fields on the entities you want indexed; in other words, what fields you want to be able to filter by. With this link definition in place, Orders and Vendors are ingested into Index whenever either side is created, updated, or deleted.
Finally, you can now put Index to use and easily filter Orders by Vendors using the Copy to clipboardquery.index
API:
123456789const { data: orders } = await query.index({entity: "order",fields: ["id", "vendor.*"],filters: {vendor: {id: "vend_01K5DSYHNTWF9N4B3Q3S4STFGN",},},});
We are continuously improving Index and will soon add both an API and an admin UI to manage data, including the ability to trigger a sync.
To try Index today, follow the steps in this post or check out the installation guide in our documentation.