npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

medusa-product-helper

v0.0.32

Published

A starter for Medusa plugins.

Readme

Compatibility

This plugin is compatible with versions >= 2.4.0 of @medusajs/medusa.

Installation

yarn add medusa-product-helper
# or
npm install medusa-product-helper

Configuration

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,
      },
      // 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 ConfigModule

Configuration 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.

Promotion Window

Configure how promotion dates are tracked using product metadata fields:

  • start_metadata_key: Product metadata field key storing promotion start date
  • end_metadata_key: Product metadata field key storing promotion end date
  • treat_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 products
  • include_preorder: Include preorder products
  • include_backorder: Include backorder products
  • include_gift_cards: Include gift cards
  • publishable_only: Only show publishable products

Rating

Configure rating-based filtering:

  • enabled: Enable rating filtering
  • min: 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 queries
  • min_orders_threshold: Minimum order count threshold to be considered a best seller (default: 1)

Filter Providers

Configure custom filter providers:

  • filterProviders: Array of file paths or module references to custom filter providers
  • disableBuiltInProviders: 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
  • Collection Filter (collection_id): Filter products by collection IDs
  • Price Range Filter (price_range): Filter products by price range (min/max)
  • Promotion Window Filter (promotion_window): Filter products by promotion date windows
  • 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)
  • Base Product Filters (base_product): Filter by ID, handle, tags, sales channel

Using Filters

All filters are available through the /store/product-helper/products endpoint:

# Filter by category
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&region_id=reg_123

# Sort by best seller status
GET /store/product-helper/products?order=best_seller&region_id=reg_123

# Best sellers in last 30 days
GET /store/product-helper/products?best_seller=true&best_seller_timeframe_from=2024-01-01&region_id=reg_123

# Best sellers with minimum order count
GET /store/product-helper/products?best_seller=true&best_seller_min_orders=5&region_id=reg_123

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 (user sessions or secret API keys). Customer tokens receive a 403 response 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:migrate

This will create the wishlist table with the following structure:

  • id: Primary key
  • customer_id: Customer identifier (searchable)
  • product_id: Product identifier (searchable)
  • created_at: Timestamp when item was added
  • updated_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): If true, returns full product details. If false, 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 to client.request and 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: Custom fetch implementation for SSR or React Native environments. Defaults to globalThis.fetch.
  • headers: Additional headers appended to every request. Required for authenticated endpoints - include Authorization: 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.

Other channels