medusa-plugin-wishlist
Adds wishlists to Medusa v2, so a shopper can keep several named lists of products, with or without an account.
Each list has a name, a description and a quantity per item.
When to use
Install it in a Medusa backend whose Evolve storefront offers wishlists. The plugin owns the lists and their Store API routes; the storefront and the GraphQL layer decide how they look.
Get started
Install the package with the peer dependencies it needs. It comes from the Evolve registry, which needs a one-time sign-in.
pnpm add @evolve-framework/medusa-plugin-wishlist @medusajs/cli @medusajs/framework @medusajs/medusaRequirements4
node>=20@medusajs/cli2.21.2@medusajs/framework2.21.2@medusajs/medusa2.21.2
For example:
// medusa-config.ts// After adding the plugin, run `npx medusa db:migrate` to create its tables.import { defineConfig } from "@medusajs/framework/utils";
module.exports = defineConfig({ // projectConfig, modules and other plugins as before plugins: [ { resolve: "@evolve-framework/medusa-plugin-wishlist", options: {} }, ],});Owners
A list belongs to either a signed-in customer or an anonymous session. The second half is the point: saving something for later is what a shopper does before they sign in, and a wishlist that needs an account is an account benefit rather than a wishlist.
Medusa has no anonymous identity: the Store API authenticates a customer or
nobody. So the caller mints an opaque id, keeps it on its own signed session
token, and sends it as x-wishlist-anonymous-id. That makes the header exactly
as strong as the session behind it, which is the same bargain Medusa already
makes for anonymous carts: holding the cart id is holding the cart.
A signed-in caller is never treated as anonymous, even if it also sends the header. A stale id left in a storefront would otherwise write to a list the customer cannot see.
POST /store/wishlists/claim moves the anonymous session's lists onto the
signed-in customer. They are moved, not merged. Two lists that happen to
share a name are not the same list, and folding them together is not something
the shopper could undo, where ending up with two is obvious and fixable in one
click. Calling it twice is harmless.
Routes
GET /store/wishlists |
the caller's lists, with items |
POST /store/wishlists |
create one |
GET /store/wishlists/:id |
one list |
POST /store/wishlists/:id |
rename or re-describe |
DELETE /store/wishlists/:id |
delete it and its items |
POST /store/wishlists/:id/items/batch |
{create, update, delete} in one call |
POST /store/wishlists/claim |
take over an anonymous session's lists |
Items are edited through one batch endpoint because that is how a storefront uses them: a list edit is a set of changes applied together, and three round trips would let a page half-apply.
A list that is not the caller's reads as absent rather than forbidden, so an id cannot be used to discover whose lists exist. Adding a variant that is already on a list raises its quantity instead of failing on the unique index, and setting a quantity to zero removes the line: a count of things to buy has no meaningful zero.
What it stores
Only the variant id and a quantity. Everything a storefront renders (name, price, availability) is read from the catalog at display time, so a list never shows a price that stopped being true the day it was saved.
Why not one of the published plugins
Several wishlist plugins exist for Medusa v2. They model a single unnamed list per customer, where the Evolve contract has several named lists with descriptions and per-item quantities, and expects them to work anonymously.

