medusa-product-helper
v0.0.55
Published
A starter for Medusa plugins.
Downloads
646
Maintainers
Readme
Overview
medusa-product-helper extends Medusa v2 with:
- Dynamic product filtering – Store and admin product list APIs with a provider-based filter system. Filter by category, collection, price range, metadata, variant options, promotions, best sellers, rating, availability, scalar product fields, variant SKU/barcode, and more.
- Scalar product filters – Filter directly on product model fields that Medusa's built-in API does not expose:
discountable,material,origin_country,hs_code,weight, and variant-level fieldssku,barcode,ean,upc. - Centralized filter registry – All filter Zod schemas live in
src/utils/product-filter-registry.ts. Adding a new filterable field means editing one file; validators derive from it automatically. - Filter descriptor format – Every filter supports either legacy query shapes or a structured descriptor
{ type, action, value }. Metadata supports per-key descriptors with actions likeexact_match,contains,in,starts_with,ends_with,not_equals. - Wishlist – Customer wishlist (store API), admin wishlist stats, and a Wishlist performance widget in the product details sidebar.
- Inventory CSV Import (Admin) - Adds an Import button on
/app/inventoryand imports exported inventory CSV rows to update existing inventory levels. - Custom filter providers – Register your own filter logic without changing plugin code.
Compatibility
This plugin is compatible with versions >= 2.4.0 of @medusajs/medusa.
Installation
yarn add medusa-product-helper
# or
npm install medusa-product-helperConfiguration
Add the plugin to your medusa-config.ts:
import type { ConfigModule } from "@medusajs/framework/types"
const plugins = [
{
resolve: "medusa-product-helper",
options: {
// Default price range when none is specified
default_price_range: {
label: "custom",
min: null,
max: null,
},
// Promotion window configuration
promotion_window: {
// Product metadata field key that stores the promotion start date
start_metadata_key: "promotion_start",
// Product metadata field key that stores the promotion end date
end_metadata_key: "promotion_end",
// If true, promotions without an end date are considered active
treat_open_ended_as_active: true,
},
// Product availability filtering options
availability: {
// Only show products that are in stock
require_in_stock: false,
// Include products available for preorder
include_preorder: true,
// Include products available for backorder
include_backorder: true,
// Include gift cards in product listings
include_gift_cards: false,
// Only show publishable products
publishable_only: true,
},
// Product rating configuration
rating: {
// Enable rating-based filtering
enabled: true,
// Minimum rating value (0-5)
min: 0,
// Maximum rating value (0-5)
max: 5,
// Require products to have at least one review
require_reviews: false,
},
// Variant option filtering configuration
variantOption: {
// Enable variant option filtering
enabled: true,
// Case-sensitive option value matching
caseSensitive: false,
// Maximum number of variant filters allowed per request (optional)
maxFilters: 10,
},
// Custom filter providers (optional)
filterProviders: [
// File path (relative to project root)
"./src/providers/margin-provider.ts",
// Or module reference
"@my-org/custom-filters/brand-provider",
],
// Disable specific built-in providers (optional)
disableBuiltInProviders: [
// "rating", // Uncomment to disable rating filter provider
],
},
},
]
export default {
projectConfig: {
// Your Medusa project configuration
// database_url: process.env.DATABASE_URL,
// ...
},
plugins,
} satisfies ConfigModuleConfiguration Options
Wishlist Insights Widget
The admin extension also injects a Wishlist performance card into the product
details sidebar (product.details.side.after). The widget:
- Shows how many unique customers saved the current product.
- Highlights whether the product ranks within the most wishlisted items.
- Displays a live leaderboard (top 5) powered by
/admin/wishlist/stats.
No additional configuration is required—install the plugin, run the wishlist migration, and open any product inside Medusa Admin to see the UI.
Customer Details Carts card
The admin extension injects a Carts card on the Customer Details page (Customers → [customer name]). The card lists cart line items (variant ID, product, quantity, unit price) from that customer’s active carts (in store, not yet ordered or checked out). The list API returns line items when called with customer_id. It is registered for:
customer.details.side.after– right column, after the Addresses sectioncustomer.details.after– main column, after Orders and Customer Groups
If the Carts card does not appear:
- Register the plugin in your main Medusa app
medusa-config.ts: add"medusa-product-helper"(or{ resolve: "medusa-product-helper", options: {} }) to thepluginsarray. - Rebuild the plugin (in the plugin repo): run
npx medusa plugin:build. - Restart or rebuild the main app: run
npm run dev(ornpx medusa develop) so the admin loads the plugin’s admin bundle; if you use a production build, runnpx medusa buildand restart. - Open Customers → any customer; the Carts card should appear in the right column (after Addresses) and/or in the main area.
Inventory CSV Import button
The admin extension injects an Import button on the inventory list page (/app/inventory) beside the default Create action (zone: inventory_item.list.before).
The button uploads a CSV file to:
POST /admin/product-helper/inventory/import
CSV requirements (matches inventory export shape: id, title, location, instock):
- Required headers:
id,location,instock - Optional column:
title(ignored during import; column may be omitted) idmust be an existing inventory item ID (theiitem_…value from export). The admin SKU column is not used for import matching.locationmay be blank on a row: the importer uses the last non-empty location above in the file (spreadsheet-style carry-down), or if the cell is blank and the item has exactly one existing stock level, that level’s location is used- When
locationis set, it must match an existing stock location (location ID or exact location name, case-insensitive) instockmust be a non-negative integer
Behavior:
- Update-only: each row must match an existing inventory level for
(inventory item id from export, stock location). OnlyupdateInventoryLevelsis used; nothing is created. - Export-compatible workflow: use the CSV from
GET /admin/export/inventory-item(same columns), editinstock(and optionallytitle—ignored). Do not add new(id, location)pairs that were not already present in the export unless you have first added that item to that location in Admin. - Atomic validation: if any row fails validation, no rows are updated.
Successful response example:
{
"message": "Inventory import completed",
"total_rows": 7,
"updated_rows": 7
}Promotion Window
Configure how promotion dates are tracked using product metadata fields:
start_metadata_key: Product metadata field key storing promotion start dateend_metadata_key: Product metadata field key storing promotion end datetreat_open_ended_as_active: If true, promotions without an end date are considered active
Note: This feature uses product metadata fields for storage. For metadata management UI and configuration, use
medusa-dynamic-metadata.
Availability
Control which products appear in listings:
require_in_stock: Only show in-stock productsinclude_preorder: Include preorder productsinclude_backorder: Include backorder productsinclude_gift_cards: Include gift cardspublishable_only: Only show publishable products
Rating
Configure rating-based filtering:
enabled: Enable rating filteringmin: Minimum rating (0-5)max: Maximum rating (0-5)require_reviews: Require at least one review
Best Seller
Configure best seller filtering and sorting:
enabled: Enable best seller filtering (default:true)default_timeframe_days: Optional default timeframe in days for best seller queriesmin_orders_threshold: Minimum order count threshold to be considered a best seller (default:1)
Variant Option
Configure variant option filtering:
enabled: Enable variant option filtering (default:true)caseSensitive: Enable case-sensitive option value matching (default:false)maxFilters: Maximum number of variant filters allowed per request (optional)
Filter Providers
Configure custom filter providers:
filterProviders: Array of file paths or module references to custom filter providersdisableBuiltInProviders: Array of provider identifiers to disable (e.g.,["rating"])
See the Provider Development Guide for details on creating custom filter providers.
Dynamic Filter Provider System
The plugin includes a provider-based dynamic filter system that allows you to extend product filtering capabilities without modifying the plugin code. All built-in filters (category, collection, price range, etc.) are implemented as filter providers, and you can add your own custom providers.
Built-in Filter Providers
The plugin includes the following built-in filter providers:
- Category Filter (
category_id): Filter products by category IDs. When you pass one or more category IDs, the API returns products in those categories and in all their child categories (descendants at every level). - Collection Filter (
collection_id): Filter products by collection IDs - Metadata Filter (
metadata): Filter products by metadata with optional descriptor format (exact_match, contains, in, etc.). Multiple keys are AND. For legacy string values, commas mean OR within that key (e.g.metadata[material]=A,Bmatches material A or B). - Price Range Filter (
price_range): Filter products by price range (min/max) - Promotion Filter (
promotion): Filter products by promotion criteria (discount, type, dates) - Promotion Window Filter (
promotion_window): Filter products by promotion date windows (metadata-based) - Availability Filter (
availability): Filter products by availability status - Rating Filter (
rating): Filter products by rating (min/max) - Best Seller Filter (
best_seller): Filter and sort products by best seller status (order count) - Variant Option Filter (
variant_option): Filter products by variant option values (e.g., color, size); OR logic across options/values - Option Value Filter (
option_value): Filter by option value strings (e.g.option_value[]=red&option_value[]=blueoroption_value[]=red,blue); product is included if any variant matches any of the values (OR) - Base Product Filters (
base_product): Filter by ID, handle, tags, sales channel - Scalar Product Filters (
scalar_product): Filter by direct product model fields (discountable,material,origin_country,hs_code,weight) and variant-level fields (sku,barcode,ean,upc) — all resolved at the DB query level
Using Filters
All filters are available through the /store/product-helper/products endpoint:
# Filter by category (includes products in that category and all child categories)
GET /store/product-helper/products?category_id=cat_123,cat_456
# Filter by price range
GET /store/product-helper/products?price_min=10&price_max=100
# Multiple filters
GET /store/product-helper/products?category_id=cat_123&price_min=10&rating_min=4
# Filter to show only best sellers
GET /store/product-helper/products?best_seller=true®ion_id=reg_123
# Sort by best seller status
GET /store/product-helper/products?order=best_seller®ion_id=reg_123
# Best sellers in last 30 days
GET /store/product-helper/products?best_seller=true&best_seller_timeframe_from=2024-01-01®ion_id=reg_123
# Best sellers with minimum order count
GET /store/product-helper/products?best_seller=true&best_seller_min_orders=5®ion_id=reg_123
# Filter by variant options (products with red variants)
GET /store/product-helper/products?[variant]color=red®ion_id=reg_123
# Filter by multiple variant option values (OR logic)
GET /store/product-helper/products?[variant]color=red&[variant]color=blue®ion_id=reg_123
# Filter by multiple variant options (OR logic - matches products with red OR large variants)
GET /store/product-helper/products?[variant]color=red&[variant]size=large®ion_id=reg_123
# Combine variant filters with other filters
GET /store/product-helper/products?[variant]color=red&category_id=cat_123&price_min=10®ion_id=reg_123
# Filter by option values (OR: variant matches red OR large)
GET /store/product-helper/products?option_value[]=red&option_value[]=large®ion_id=reg_123
# Same OR semantics with a comma-separated array entry (e.g. Beige or Blue)
GET /store/product-helper/products?option_value[]=Beige,Blue®ion_id=reg_123
# --- Scalar product & variant field filters ---
# Only discountable products
GET /store/product-helper/products?discountable=true®ion_id=reg_123
# Products made of a specific material (single value)
GET /store/product-helper/products?material=cotton®ion_id=reg_123
# Products made of cotton OR linen (comma-separated or repeated params)
GET /store/product-helper/products?material=cotton,linen®ion_id=reg_123
# Filter by country of origin
GET /store/product-helper/products?origin_country=US®ion_id=reg_123
# Filter by HS code
GET /store/product-helper/products?hs_code=6109100010®ion_id=reg_123
# Filter by weight (exact match)
GET /store/product-helper/products?weight=500®ion_id=reg_123
# Filter by variant SKU
GET /store/product-helper/products?sku=SKU-001®ion_id=reg_123
# Multiple SKUs
GET /store/product-helper/products?sku=SKU-001,SKU-002®ion_id=reg_123
# Filter by barcode
GET /store/product-helper/products?barcode=0123456789®ion_id=reg_123
# Filter by EAN
GET /store/product-helper/products?ean=4006381333931®ion_id=reg_123
# Filter by UPC
GET /store/product-helper/products?upc=012345678905®ion_id=reg_123
# Combine scalar filters with other filters
GET /store/product-helper/products?discountable=true&material=cotton&origin_country=US®ion_id=reg_123Note: Store requests require region_id (used for currency and pricing). Admin requests do not.
Variant Option Filtering
The variant option filter allows you to filter products by variant properties like color, size, material, etc. It uses a special query parameter syntax: [variant]optionName=value.
Key Features:
- OR Logic: Products are returned if ANY of their variants match ANY of the filter conditions
- Case-Insensitive: Option names and values are matched case-insensitively by default (configurable)
- Multiple Values: You can specify multiple values for the same option (e.g., red OR blue)
- Multiple Options: You can filter by multiple different options (e.g., color OR size)
Examples:
# Products with any red variant
GET /store/product-helper/products?[variant]color=red®ion_id=reg_123
# Products with red OR blue variants (multiple values)
GET /store/product-helper/products?[variant]color=red&[variant]color=blue®ion_id=reg_123
# Products with red variants OR large variants (multiple options)
GET /store/product-helper/products?[variant]color=red&[variant]size=large®ion_id=reg_123
# Combine with other filters
GET /store/product-helper/products?[variant]color=red&category_id=cat_shoes&price_max=100®ion_id=reg_123Admin API:
The same variant filtering is available on the admin endpoint without region requirement:
# Admin: Filter by variant options
GET /admin/product-helper/products?[variant]color=red
# Admin: Multiple variant filters
GET /admin/product-helper/products?[variant]color=red&[variant]size=large&category_id=cat_123Scalar Product & Variant Filters
These filters are applied directly at the database query level (no post-query in-memory pass) via the ScalarProductFilterProvider (identifier: scalar_product).
Product-level scalar fields:
| Query param | Type | Description |
|---|---|---|
| discountable | boolean | Include (true) or exclude (false) discountable products |
| material | string or string[] | Filter by product material (e.g. cotton, linen) |
| origin_country | string or string[] | Filter by country of origin (e.g. US, DE) |
| hs_code | string or string[] | Filter by Harmonized System tariff code |
| weight | number | Filter by exact product weight |
Variant-level scalar fields (resolved as variants.field in the DB query):
| Query param | Type | Description |
|---|---|---|
| sku | string or string[] | Filter by variant SKU(s) |
| barcode | string or string[] | Filter by variant barcode(s) |
| ean | string or string[] | Filter by variant EAN(s) |
| upc | string or string[] | Filter by variant UPC(s) |
All string-array fields accept either a comma-separated string (material=cotton,linen) or a repeated query param (material[]=cotton&material[]=linen). The boolean discountable field accepts true/false, 1/0, yes/no, or y/n.
Examples:
# Only non-discountable products
GET /store/product-helper/products?discountable=false®ion_id=reg_123
# Products from multiple countries
GET /store/product-helper/products?origin_country=US,DE®ion_id=reg_123
# Find a product by SKU (useful for cart recovery or deep-link lookup)
GET /store/product-helper/products?sku=SHIRT-RED-L®ion_id=reg_123
# Combine: cotton products from US that are discountable
GET /store/product-helper/products?material=cotton&origin_country=US&discountable=true®ion_id=reg_123Admin API: All scalar filters are available on the admin endpoint without region_id:
GET /admin/product-helper/products?sku=SKU-001
GET /admin/product-helper/products?discountable=true&material=cottonFilter descriptor format
Every filter parameter can be sent either in its legacy shape or as a descriptor object { type, action, value }. For structured filters (e.g. price_range, promotion, best_seller), the descriptor is a wrapper: value is the same object as before. For value/list filters (category_id, collection_id, option_value, variant_option), value is the list or scalar; for metadata, each key’s value can be a descriptor with full matching semantics.
Descriptor fields:
| Field | Allowed values |
| -------- | ----------------- |
| type | string, int, number, boolean (or object for structured filters) |
| action | exact_match, contains, starts_with, ends_with, in, not_equals |
| value | Primitive, array, or object (same shape as the filter’s legacy value) |
Metadata supports per-key descriptors with full matching (exact_match, contains, in, etc.). Other filters accept a single top-level descriptor and unwrap to the legacy shape; variant_option can have a descriptor per option (unwrap to string[] per key).
Examples (send descriptor as URL-encoded JSON in query):
# Store: region_id is required
BASE="https://your-store.com/store/product-helper/products?region_id=reg_01ABC"
# category_id as descriptor
curl -X GET "$BASE&category_id=%7B%22type%22%3A%22string%22%2C%22action%22%3A%22in%22%2C%22value%22%3A%5B%22cat_1%22%2C%22cat_2%22%5D%7D"
# metadata per-key descriptor (exact_match)
curl -X GET "$BASE&metadata=%7B%22color%22%3A%7B%22type%22%3A%22string%22%2C%22action%22%3A%22exact_match%22%2C%22value%22%3A%22red%22%7D%7D"
# metadata contains
# metadata = { "material": { "type": "string", "action": "contains", "value": "Cotton" } }
# price_range as descriptor (value = same object as legacy)
# price_range = { "type": "number", "action": "exact_match", "value": { "min": 10, "max": 100 } }Admin: Same filter and descriptor support at GET /admin/product-helper/products (no region_id required).
Backward compatibility: all existing requests using legacy values (e.g. category_id=cat_1,cat_2, metadata[color]=red, [variant]color=red) continue to work unchanged.
Filter Registry — Adding New Filters
All Zod validation schemas for filterable fields live in a single centralized file:
src/utils/product-filter-registry.tsIt exports three schemas that validators import and .merge() from:
| Export | Fields |
|---|---|
| ProductScalarFilterSchema | discountable, material, origin_country, hs_code, weight |
| ProductVariantScalarFilterSchema | sku, barcode, ean, upc |
| Shared primitives | booleanish, stringArray, optionalNumberish |
To add a new scalar DB-level filter in one change:
- Add the field to the appropriate schema in
product-filter-registry.ts. - Add the field to
ScalarProductFilterProvider.apply()inscalar-product-provider.ts. - Add extraction in
ProductFilterService.extractScalarFilters().
No changes needed to DynamicFilterService, FilterProviderRegistry, or validators.ts (the validator auto-derives from the merged schema).
Custom Filter Providers
You can create custom filter providers to add new filtering capabilities. See the Provider Development Guide for complete documentation.
Quick Example:
// src/providers/margin-provider.ts
import { BaseFilterProvider } from "medusa-product-helper/providers/filter-providers"
export class MarginFilterProvider extends BaseFilterProvider {
static readonly identifier = "margin"
static readonly displayName = "Margin Filter"
apply(filters: Record<string, unknown>, value: unknown): Record<string, unknown> {
if (!value || typeof value !== "object") return filters
const { min, max } = value as { min?: number; max?: number }
// Apply margin filter logic...
return filters
}
}Then register it in medusa-config.ts:
{
resolve: "medusa-product-helper",
options: {
filterProviders: ["./src/providers/margin-provider.ts"],
},
}Wishlist Feature
The plugin includes a comprehensive wishlist feature that allows customers to save products they're interested in and admins to view wishlist statistics.
Security & Access Control
- Store-facing wishlist routes authenticate strictly as customers. Customer IDs are always derived from the session or JWT auth context and never taken from the request payload, so an admin or API client cannot spoof a customer identifier.
- Admin-facing wishlist statistics are available only to admin actors (
usersessions or secret API keys). Customer tokens receive a403response when attempting to access/admin/wishlist/stats. - This separation ensures only customers can manage their wishlist entries while only admins can inspect aggregate wishlist state.
Module Registration
The wishlist module is automatically registered when you add the plugin to your Medusa configuration. No additional configuration is required for basic usage.
Database Migration
After installing the plugin, generate and run the database migration:
npx medusa plugin:db:generate
npx medusa db:migrateThis will create the wishlist table with the following structure:
id: Primary keycustomer_id: Customer identifier (searchable)product_id: Product identifier (searchable)created_at: Timestamp when item was addedupdated_at: Timestamp when item was last updated
The table enforces a unique constraint on the combination of customer_id and product_id, ensuring no duplicate entries.
Store API Endpoints
Add Product to Wishlist
POST /store/wishlist
Add a product to the current customer's wishlist. The operation is idempotent - adding the same product multiple times will not create duplicates.
Authentication: Required (customer must be authenticated)
Request Body:
{
"product_id": "prod_123"
}Response:
{
"wishlist_item": {
"id": "wish_123",
"customer_id": "cus_123",
"product_id": "prod_123",
"created_at": "2024-01-01T00:00:00.000Z",
"updated_at": "2024-01-01T00:00:00.000Z"
}
}Example:
curl -X POST https://your-store.com/store/wishlist \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"product_id": "prod_123"}'Remove Product from Wishlist
DELETE /store/wishlist/:product_id
Remove a product from the current customer's wishlist.
Authentication: Required (customer must be authenticated)
Response:
{
"success": true
}Example:
curl -X DELETE https://your-store.com/store/wishlist/prod_123 \
-H "Authorization: Bearer YOUR_TOKEN"Get Customer Wishlist
GET /store/wishlist
Get the current customer's wishlist. Can return either product IDs only or full product details.
Authentication: Required (customer must be authenticated)
Query Parameters:
include_details(boolean, default:false): Iftrue, returns full product details. Iffalse, returns only product IDs.
Response (IDs only):
{
"wishlist": [
{
"product_id": "prod_123"
},
{
"product_id": "prod_456"
}
]
}Response (with details):
{
"wishlist": [
{
"id": "wish_123",
"product_id": "prod_123",
"product": {
"id": "prod_123",
"title": "Product Name",
"handle": "product-name",
"description": "Product description",
"thumbnail": "https://example.com/image.jpg",
"status": "published",
"created_at": "2024-01-01T00:00:00.000Z",
"updated_at": "2024-01-01T00:00:00.000Z"
},
"created_at": "2024-01-01T00:00:00.000Z"
}
]
}Example:
# Get only product IDs
curl https://your-store.com/store/wishlist \
-H "Authorization: Bearer YOUR_TOKEN"
# Get full product details
curl "https://your-store.com/store/wishlist?include_details=true" \
-H "Authorization: Bearer YOUR_TOKEN"Admin API Endpoints
Get Wishlist Statistics
GET /admin/wishlist/stats
Get wishlist statistics showing how many users have added each product to their wishlist.
Authentication: Required (admin authentication)
Query Parameters:
product_id(string, optional): If provided, returns the wishlist count for that specific product only.
Response (all products):
{
"stats": [
{
"product_id": "prod_123",
"wishlist_count": 42
},
{
"product_id": "prod_456",
"wishlist_count": 15
}
]
}Response (single product):
{
"product_id": "prod_123",
"wishlist_count": 42
}Example:
# Get statistics for all products
curl https://your-store.com/admin/wishlist/stats \
-H "Authorization: Bearer ADMIN_TOKEN"
# Get statistics for a specific product
curl "https://your-store.com/admin/wishlist/stats?product_id=prod_123" \
-H "Authorization: Bearer ADMIN_TOKEN"Helper Functions
The plugin exposes lightweight helpers that wrap the Store API endpoints. They are ideal for server-side storefronts that need to call the wishlist endpoints while relying on the authenticated customer's session (no customer ID is ever passed in the payload).
Imports
import {
addToWishlist,
getWishlist,
removeFromWishlist,
createWishlistHelpers,
type HelperTransportOptions,
type WishlistItem,
} from "medusa-product-helper/helpers"Example Usage
// Recommended: Using Bearer token for authentication
const options: HelperTransportOptions = {
baseUrl: "https://store.example.com",
publishableApiKey: "pk_your_publishable_api_key",
headers: {
Authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", // Customer JWT token
},
}
// Add a product
await addToWishlist({ product_id: "prod_123" }, options)
// Read wishlist with details
const { wishlist } = await getWishlist({ includeDetails: true }, options)
// Remove a product
await removeFromWishlist({ product_id: "prod_123" }, options)Alternative: Using session cookie
const options: HelperTransportOptions = {
baseUrl: "https://store.example.com",
publishableApiKey: "pk_your_publishable_api_key",
headers: {
Cookie: "connect.sid=...", // Session cookie (browser-based)
},
}Configuration Options
All helper calls accept the following options:
publishableApiKey: Required for public storefront calls when not passing a Medusa JS client. Add it from Settings → API Keys in the Medusa dashboard.client: Medusa JS/SDK client instance. When provided, network requests are delegated toclient.requestand the client's publishable key is reused.baseUrl: Base URL for the Store API (e.g.,https://store.example.com). Required when a client is not provided.fetchImpl: Customfetchimplementation for SSR or React Native environments. Defaults toglobalThis.fetch.headers: Additional headers appended to every request. Required for authenticated endpoints - includeAuthorization: Bearer <token>for customer authentication:headers: { "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }
Note: All wishlist endpoints require customer authentication. Pass the Bearer token via the headers option.
You can also generate pre-configured helpers:
// Recommended: Using Bearer token
const wishlist = createWishlistHelpers({
baseUrl: "https://store.example.com",
publishableApiKey: "pk_your_publishable_api_key",
headers: {
Authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
},
})
await wishlist.addToWishlist({ product_id: "prod_123" })
const { wishlist: items } = await wishlist.getWishlist({ includeDetails: true })
await wishlist.removeFromWishlist({ product_id: "prod_123" })Getting the Bearer Token:
// Login to get JWT token
const loginResponse = await fetch("https://store.example.com/store/auth/customer/emailpass", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-publishable-api-key": "pk_your_publishable_api_key",
},
body: JSON.stringify({
email: "[email protected]",
password: "password123",
}),
})
const { token } = await loginResponse.json()
// Use token in wishlist helpers
const wishlist = createWishlistHelpers({
baseUrl: "https://store.example.com",
publishableApiKey: "pk_your_publishable_api_key",
headers: {
Authorization: `Bearer ${token}`,
},
})Using Workflows Directly
You can also use the workflows directly in your custom code:
import { addToWishlistWorkflow } from "medusa-product-helper/workflows"
import { removeFromWishlistWorkflow } from "medusa-product-helper/workflows"
import { getWishlistWorkflow } from "medusa-product-helper/workflows"
// Add to wishlist
const { result } = await addToWishlistWorkflow(container).run({
input: {
customer_id: "cus_123",
product_id: "prod_123"
}
})
// Remove from wishlist
const { result } = await removeFromWishlistWorkflow(container).run({
input: {
customer_id: "cus_123",
product_id: "prod_123"
}
})
// Get wishlist
const { result } = await getWishlistWorkflow(container).run({
input: {
customer_id: "cus_123",
options: {
includeDetails: true
}
}
})Using the Module Service Directly
You can also access the wishlist module service directly:
import { WISHLIST_MODULE } from "medusa-product-helper/modules/wishlist"
import type { WishlistModuleService } from "medusa-product-helper/modules/wishlist"
// In your route or service
const wishlistService: WishlistModuleService = container.resolve(WISHLIST_MODULE)
// Add to wishlist
await wishlistService.addToWishlist(customerId, productId)
// Remove from wishlist
await wishlistService.removeFromWishlist(customerId, productId)
// Get wishlist
const wishlist = await wishlistService.getWishlist(customerId, {
includeDetails: true
})
// Check if product is in wishlist
const isInWishlist = await wishlistService.isInWishlist(customerId, productId)
// Get wishlist counts (for admin)
const counts = await wishlistService.getWishlistCounts(['prod_123', 'prod_456'])Getting Started
Visit the Quickstart Guide to set up a server.
Visit the Plugins documentation to learn more about plugins and how to create them.
What is Medusa
Medusa is a set of commerce modules and tools that allow you to build rich, reliable, and performant commerce applications without reinventing core commerce logic. The modules can be customized and used to build advanced ecommerce stores, marketplaces, or any product that needs foundational commerce primitives. All modules are open-source and freely available on npm.
Learn more about Medusa’s architecture and commerce modules in the Docs.
Community & Contributions
The community and core team are available in GitHub Discussions, where you can ask for support, discuss roadmap, and share ideas.
Join our Discord server to meet other community members.
