# Migrate from Algolia to Medusa Search

You are an AI coding agent. The prompt below migrates a Medusa project from a custom Algolia integration to [Medusa Search](../page.mdx), the managed search service of [Cloud](../../page.mdx). Follow it end-to-end in the user's project, then report the manual follow-ups it lists.

```md title="Migration Prompt"
<role>
You are a senior Medusa developer migrating an existing
Medusa project from a custom Algolia integration to Medusa
Search, the managed search service of Cloud.
</role>

<task>
Replace this project's custom Algolia search integration
with the Search Module and Medusa Search, keeping the
storefront's search behavior intact.
</task>

<context>
- Medusa Search is a provider of the Search Module, which
  ships with Medusa v2.21.1 and later. Cloud registers and
  configures it for every environment, so it needs no
  credentials or configuration in the project.
- Medusa declares no index by default, so a project
  declares its own `product` index definition under
  `src/search`. A project installed after v2.21.1 ships with
  one at `src/search/product.ts`, and any other project may
  or may not have it.
- An index definition lives in a file under `src/search`,
  declares the fields the engine holds, fills the index with
  a `seed` async generator, and keeps it current with its
  `events` and `consume` properties.
- The `graphSeed` and `graphConsume` helpers build `seed`
  and `consume` from Query, so an index of an entity Query
  exposes needs neither written by hand. They take the same
  options, including a `transform` that maps a page of
  records to the documents to index. Every document must
  carry the record's primary key as `id`, and a record you
  leave out of the returned array stays out of the index.
- Medusa's Store API has a `POST /store/search` route that
  searches any index of the application, so a project
  doesn't need its own product search route. The body is a
  search query naming the index in `entity`, or a batch of
  them under `queries`.
- An index is only reachable through `POST /store/search`
  once a middleware allows it with `configureStoreSearch`
  from `@medusajs/framework/http`, which takes an
  `allowed_indexes` object. The route narrows a product
  index to published products in the publishable API key's
  sales channels, and any further constraint is that
  middleware's `filters` option.
- Queries run through `query.search` in an API route. There
  is no browser-side querying and no search-only key.
- The `@medusajs/instantsearch-adapter` package is a search
  client for InstantSearch widgets, so a storefront built on
  `react-instantsearch` keeps its widgets and swaps the
  client.
- Medusa Search does not support synonyms, merchandising
  rules, geo search, or search analytics. An index or a query
  that relies on them fails.
- Medusa Search supports typo tolerance and highlighting, but
  both are opt-in per query through `search_options` and both
  require a text query.
</context>

<steps>
1. Inspect the project and report what it has before you
   change anything: the Algolia module, the sync workflows
   and their steps, the subscribers, the admin sync route
   and UI route, the search API route, the storefront search
   client, and the Medusa version in `package.json`.
2. If the Medusa version is below v2.21.1, stop and tell the
   user to upgrade first.
3. List the fields the project indexes in Algolia, taken
   from the sync workflow's `fields` array, and ask the user
   for the index settings from their Algolia dashboard.
4. Check whether the project already declares a `product`
   index, which ships at `src/search/product.ts`. If it
   does and every indexed field and setting is covered by
   it, skip to step 6 and say why. Otherwise, the project
   needs an index definition of its own.
5. Create the index definition under `src/search`. Map
   Algolia's searchable attributes to `searchable()` fields
   with a weight, faceting attributes to `facetable()` or
   `filterable()`, sorting replicas to `sortable()`, and
   unretrievable attributes to `retrievable(false)`. Build
   `seed` and `consume` with `graphSeed` and `graphConsume`,
   sharing one options object between them, and reproduce
   the project's indexing rules in its `transform`, such as
   leaving a product that isn't published out of the
   documents it returns. List
   the entity's create, update, and delete events in
   `events`.
6. Allow every index the storefront searches on
   `/store/search` with the `configureStoreSearch`
   middleware in `src/api/middlewares.ts`. An index the
   middleware doesn't allow answers exactly like one that
   doesn't exist. The route narrows a product index to
   published products in the publishable API key's sales
   channels, so add a `filters` option only for a further
   constraint.
7. Delete the project's product search API route and its
   validation middleware, since `POST /store/search`
   replaces it. Only write a route with `query.search` for a
   result the built-in route can't answer with, such as one
   reshaped for the storefront. In such a route, respond
   with the hydrated records from `data` and the metadata
   from `search_result`, and don't also return
   `search_result.hits`, since each hit's `document` repeats
   a record already returned.
8. Replace any manual sync trigger with a workflow step that
   calls the Search Module's `reindex` method.
9. Delete the Algolia module, the sync workflows and steps,
   the product subscribers, the module's entry in
   `medusa-config.ts`, and the `algoliasearch` dependency.
   Do this here and not earlier, since the application fails
   to boot while the module is still registered and the
   package is gone. Leave the Algolia environment variables
   in place and tell the user to remove them after the
   cutover.
10. Update the storefront to search through Medusa. If it
    uses InstantSearch widgets, install
    `@medusajs/instantsearch-adapter`, create its search
    client with the storefront's JS SDK instance and the
    path `/store/search`, pass the client and the index
    name to the existing `InstantSearch` provider, and
    change every component that reads a hit's `objectID` to
    read `id`. Otherwise, post to `/store/search` with the
    JS SDK and read the `hits` and `metadata` it returns. Remove the Algolia packages, the Algolia search
    client, and the Algolia environment variables from the
    storefront.
11. Run `npx medusa db:migrate --execute-all-links` to
    create the index locally, then run the project's type
    check and tests, and report the result. Never run
    `medusa db:migrate` without that flag: it asks which
    link tables to sync, and the prompt is swallowed when
    the command's output is piped, so it waits forever.
</steps>

<constraints>
- Do not add a feature that Medusa Search does not support.
  When the project relies on one, report it and propose the
  closest alternative instead of implementing it silently.
- Do not delete the user's Algolia account, indexes, or
  credentials, and do not call the Algolia API.
- Do not modify `POST /store/search` or recreate it in the
  project.
- Do not refine a widget or a filter on a field the index
  definition doesn't mark `searchable()`, `filterable()`,
  `facetable()`, or `sortable()`.
- Do not run a deployment or push to any branch.
- Never run a Medusa CLI command that can prompt without the
  flag that skips its prompts. A swallowed prompt reads as a
  hung command rather than an error, since the command keeps
  waiting with no output.
- Keep every package the current integration imports
  installed until step 9, so the application boots at every
  point before it.
- Consult the Medusa documentation at
  https://docs.medusajs.com or the Medusa MCP server for any
  API details you need, including the index definition
  properties, field types and modifiers, the options of
  `graphSeed` and `graphConsume`, and the options of
  `query.search`.
</constraints>

<error_handling>
- If the project has no Algolia integration, report that and
  stop.
- If the integration differs from the files listed in step
  1, map each responsibility you find to its Search Module
  equivalent and report the mapping before you change code.
- If you cannot tell whether a field is searched, filtered,
  faceted, or only displayed, ask the user rather than
  guessing its modifiers.
- If a storefront feature has no Medusa Search equivalent,
  list it under manual follow-ups instead of removing the
  feature. The adapter doesn't support `geoSearch`, Insights
  and Analytics widgets, Query Rules, related-items widgets,
  autocomplete, or Algolia `filters` strings.
- If the storefront's search can't be expressed with the
  built-in route's parameters, ask the user before adding a
  custom search route for products.
</error_handling>

<output_format>
Respond with the following markdown sections:

## Changes
A table of every file you created, modified, or deleted,
with one sentence on what changed in it.

## Behavior differences
Each Algolia behavior the project relied on that Medusa
Search does not support, and what you did about it.

## Manual follow-ups
The steps the user has to take themselves, such as removing
environment variables, validating relevance, and deploying.
</output_format>

<success_criteria>
- The project builds and type checks with no reference to
  `algoliasearch` left in the backend or the storefront.
- Every field the project indexed in Algolia is either held
  by the new index definition, covered by the default
  product index, or listed as an intentional removal.
- Every index the storefront searches is named in an
  `configureStoreSearch` middleware on `/store/search`.
- The storefront searches products through
  `POST /store/search` and renders the hits it returns.
- Every remaining search route uses `query.search` and
  returns each record once.
- Every unsupported Algolia behavior appears under
  "Behavior differences".
</success_criteria>
```

***

## After the Migration

The steps below need the user's Cloud account and their judgment, so hand them over rather than running them:

1. **Deploy to a preview environment.** Push the changes to the branch of a [preview environment](../../environments/preview/page.mdx) rather than production. Cloud runs the search migrations during the deployment, then the Search Module fills the index with the documents that `seed` yields when the application starts.
2. **Confirm the index filled.** Check the environment's [logs](../../logs/page.mdx) for `Search indexes migrated`, then send a request to the search route and confirm it returns results. If it returns none, query the `search_index_sync` table to see whether the seed ran and what it failed with.
3. **Validate before the cutover.** Compare both engines on the same environment: the top customer queries, every filter and facet, the index's document count against the published products, the sync after creating and deleting a product, and the admin dashboard's global search.
4. **Deploy to production.** Merge into the branch of the production environment once the preview environment holds up. Refer to the [Deployments](../../deployments/page.mdx) guide.
5. **Remove the Algolia credentials.** Delete the `ALGOLIA_*` and `NEXT_PUBLIC_ALGOLIA_*` environment variables from the project and the storefront, and remove the Algolia account's indexes.

***

## Reference

- [Medusa Search vs Algolia](../algolia/page.mdx): a feature-by-feature comparison, including the Algolia features that Medusa Search doesn't offer.
- [Search Module](https://docs.medusajs.com/resources/infrastructure-modules/search): the module's documentation, including [index definitions](https://docs.medusajs.com/resources/infrastructure-modules/search/index-definitions) and [reindexing](https://docs.medusajs.com/resources/infrastructure-modules/search/reindexing).
- [`query.search`](https://docs.medusajs.com/learn/fundamentals/query/search): the API behind `POST /store/search`, for a custom route that reshapes a search result.
- [InstantSearch Adapter](https://docs.medusajs.com/resources/instantsearch): the storefront search client for InstantSearch widgets.
