Skip to main content
Mercur connects Medusa’s Product module to its own modules through module links defined in packages/core/src/links. Read across a link with query.graph and the link’s alias. Read-only links have no pivot table: they resolve from a foreign key column on one side, and you change them by updating that column. Resolve query from the container before you run the examples:

Product and Seller

The store allowlist. A product with no rows is open to every store.
  • Sides: Product (many) and Seller (many)
  • Storage: pivot table product_seller with product_id and seller_id
  • Alias on product: sellers
  • File: product-seller-link.ts
Write it with linkSellersToProductWorkflow or the seller_ids input of createProductsWorkflow.

ProductCategory and Seller

Links stores to categories. No shipped route filters products by it.
  • Sides: ProductCategory (many) and Seller (many)
  • Storage: pivot table generated by Medusa
  • Alias on category: sellers
  • File: category-seller-link.ts
Write it with linkSellersToProductCategoryWorkflow.

Offer and Product

Each offer points at the master product it sells.
  • Sides: Offer (one) to Product (one)
  • Storage: read-only, through offer.product_id
  • Alias on offer: product
  • File: offer-product-link.ts
To list a product’s offers, query offer filtered by product_id, as above.

Offer and ProductVariant

Each offer sells exactly one variant.
  • Sides: Offer (one) to ProductVariant (one)
  • Storage: read-only, through offer.variant_id
  • Alias on offer: product_variant
  • File: offer-variant-link.ts

Product and ProductChange

The audit and edit history of a product, including every PRODUCT_ADD and STATUS_CHANGE record.
  • Sides: Product (one) to ProductChange (many)
  • Storage: read-only, through product_change.product_id
  • Alias on product: changes
  • File: product-change-link.ts

Product and ProductAttributeValue

The attribute values attached to a product.
  • Sides: Product (many) and ProductAttributeValue (many)
  • Storage: pivot table product_attribute_value_link
  • Alias on product: product_attribute_values
  • File: product-attribute-value-pivot-link.ts

Product and scoped ProductAttribute

Attributes created inline for one product.
  • Sides: Product (one) to ProductAttribute (many)
  • Storage: read-only, through product_attribute.product_id
  • Alias on product: scoped_attributes
  • File: product-attribute-product-link.ts

ProductAttribute and ProductCategory

Assigns attributes to categories.
  • Sides: ProductAttribute (many) and ProductCategory (many)
  • Storage: pivot table product_category_attribute
  • Alias on attribute: categories
  • File: product-attribute-category-link.ts

Attribute option mirrors

A variant-axis attribute mirrors a native option, and each of its values mirrors an option value. Both links are one-to-one and read-only:
  • ProductAttribute to ProductOption: through product_attribute.product_option_id, in product-attribute-option-mirror-link.ts
  • ProductAttributeValue to ProductOptionValue: through product_attribute_value.product_option_value_id, in product-attribute-value-option-value-mirror-link.ts
Read the ids from the attribute side:

Product and Review

Customer reviews of a product.
  • Sides: Product (one) to Review (many)
  • Storage: pivot table generated by Medusa
  • Alias on product: reviews
  • File: product-review.ts

ProductCategory and ProductCollection images

Categories and collections own gallery and icon images from Mercur’s media module.
  • Sides: ProductCategory (one) to MediaImage (many), and ProductCollection (one) to MediaImage (many)
  • Alias: media_images on both
  • Files: media-product-category-link.ts and media-product-collection-link.ts
Don’t define a link with the alias images on a category or collection. Both models live in the Product module, and that alias shadows Product.images, which breaks every product query.