Skip to main content
The Product Attribute module owns configurable attributes and their allowed values — the structured properties that describe products (material, color, size, condition, …). Attributes can be global (shared across the catalog) or product-scoped, can be restricted to categories, and — for multi_select attributes — can act as variant axes backed by Medusa’s native global product options.

Data models

ProductAttribute

Table product_attribute, ID prefix pattr.
FieldTypeNotes
nametextSearchable
handletextNullable; unique when set; auto-generated for global attributes
descriptiontextNullable
typeenumAttributeType; immutable after creation
is_requiredbooleanDefault false
is_filterablebooleanDefault false; filterable attributes are tokenized into the search index
is_variant_axisbooleanDefault false; only valid for multi_select
ranknumberDefault 0; display ordering
is_activebooleanDefault true
product_idtextNullable — null means global, set means product-scoped (inline)
product_option_idtextNullable; mirror to the native ProductOption for axis attributes
created_bytextNullable
metadatajsonNullable

ProductAttributeValue

Table product_attribute_value, ID prefix pattrval. Unique on (attribute_id, handle) when the handle is set.
FieldTypeNotes
nametextDisplay label
handletextNullable; auto-generated for global select attributes
ranknumberDefault 0
is_activebooleanDefault true
product_option_value_idtextNullable; mirror to the native ProductOptionValue
metadatajsonNullable

Enums

enum AttributeType {
  SINGLE_SELECT = "single_select",
  MULTI_SELECT = "multi_select",
  UNIT = "unit",
  TOGGLE = "toggle",
  TEXT = "text",
}

How each type is stored

TypeVariant axis?Backing store
multi_select (axis)yesNative Medusa ProductOption + option values; the product links a subset of values
multi_select / single_select (non-axis)noProduct ↔ value link rows
text / unitnoA value record is created for the entered text, then linked
togglenoTwo seeded fixed values (true / false); the matching one is linked
Only multi_select attributes may be variant axes. A global axis maps to a shared (is_exclusive: false) product option; an inline, product-scoped axis maps to an exclusive option.
LinkPurpose
product_category_attributeRestricts an attribute to categories (many-to-many)
product_attribute_value_linkProduct ↔ selected values (non-axis selections, and a mirror of axis selections)
scoped_attributes (read-only)Product → its product-scoped attributes via product_id
Option mirror (read-only)ProductAttribute.product_option_id → native ProductOption
Option-value mirror (read-only)ProductAttributeValue.product_option_value_id → native ProductOptionValue

Service

ProductAttributeModuleService extends MedusaService with auto-generated CRUD, plus:
MethodBehavior
createProductAttributes(data)Auto-generates handles for global attributes; seeds true/false values for toggle attributes
updateProductAttributes(data)Rejects any attempt to change an attribute’s type
listProductAttributes / listAndCountProductAttributesAttach values only for select/toggle types; other types return values: []
createProductAttributeValues(data)Auto-generates value handles for global select attributes

Attaching attributes to products

Attributes are applied to products through the batch endpoint rather than the module service directly:
POST /admin/products/:id/attributes/batch
POST /vendor/products/:id/attributes/batch
The body is { add?, remove?, update? }, applied in the order remove → add → update by the createAndLinkProductAttributesToProductWorkflow. Product create and update payloads also accept a unified attributes[] array. Product GET responses expose the attribute graph as native options (variant axes), product_attribute_values (each with its parent attribute and, for global selects, all_values), and scoped_attributes.
  • GET/POST /admin/product-attributes, value sub-routes — catalog management
  • GET /vendor/product-attributes — category-filtered read access for sellers
  • GET /store/product-attributes — storefront filter facets

Next steps

Product Edit module

Search module