Introducing custom attributes in Medusa.js (Part 1)
The challenge
In our journey to create custom stores using Medusa.js in Rigby, we encountered a challenge with adding custom attributes to our products. Medusa.js lacked a built-in custom attribute API, so we had to resort to using the metadata field with key-value pairs to store our attributes. However, filtering these attributes proved to be a complex task, as they could be of various types, such as arrays, strings, or numbers. We turned to PostgreSQL JSONB queries, but this approach came with its own set of issues.
query.andWhere(
`product.metadata->’attributes’->>’designer’ IN (:…designers)`,
{
designers,
}
);
Challenges with this approach included:
1. Having to extend the query for each new attribute.
2. Handling attributes that depend on product categories.
3. Dealing with slow JSONB queries.
Solution
To address these limitations, we developed a new plugin, “medusa-custom-attributes,” which adds a custom attributes API to Medusa.js. Here’s how you can use it:
Install plugin:
npm install medusa-plugin-custom-attributes
// OR
yarn add medusa-plugin-custom-attributes
If you use product categories, enable them in your .env
file:
MEDUSA_FF_PRODUCT_CATEGORIES=true
Next, add the plugin to your medusa-config.js file as follows:
const plugins = [
// ...
{
resolve: `medusa-custom-attributes`,
options: {
enableUI: true,
projectConfig: {
store_cors: STORE_CORS,
admin_cors: ADMIN_CORS,
},
},
},
]
And run migrations:
npx medusa migrations run
After successfully adding the plugin, you can access the “Custom attributes” option in your admin panel.
Click add attribute
medusa-custom-attributes
has 3 types of attributes:
- Single — Allows you to define 1 attribute value from options.
- Multiple — Allows you to multiple attribute values from options
- Boolean
- Range — Integer
After adding an attribute and selecting categories (or leaving the categories field empty for a global attribute), you can select them in the product details page under the “Attributes” widget.
Using on the storefront
You can list attributes by accessing the /store/attributes
route. In the response, attributes with the "filterable" field set to true are filterable. You can also filter attributes by categories using query parameters, like this:
/store/attributes?categories[0]=t-shirts
You’ll receive a response with attribute details and their values.
[
{
"id": "attr_01HE7NV34RT5E9HKGNQG0DJ551",
"created_at": "2023-11-02T09:29:14.642Z",
"updated_at": "2023-11-02T09:29:14.642Z",
"name": "Style",
"description": "Style attribute",
"type": "multi",
"handle": "style",
"filterable": true,
"metadata": null,
"values": [
{
"id": "attr_val_01HE7NV34S6A6MR9ENT7RZF66Q",
"created_at": "2023-11-02T09:29:14.642Z",
"updated_at": "2023-11-02T09:29:14.642Z",
"value": "Vintage",
"metadata": null,
"rank": 0
},
{
"id": "attr_val_01HE7NV34SKH8NBQFCMM48KMDW",
"created_at": "2023-11-02T09:29:14.642Z",
"updated_at": "2023-11-02T09:29:14.642Z",
"value": "Casual",
"metadata": null,
"rank": 1
},
{
"id": "attr_val_01HE7NV34SZMADRN0S70M4ECA7",
"created_at": "2023-11-02T09:29:14.642Z",
"updated_at": "2023-11-02T09:29:14.642Z",
"value": "Streetwear",
"metadata": null,
"rank": 2
},
{
"id": "attr_val_01HE7NV34SZ95MCNYZPTTQVSF3",
"created_at": "2023-11-02T09:29:14.642Z",
"updated_at": "2023-11-02T09:29:14.642Z",
"value": "Formal",
"metadata": null,
"rank": 3
}
],
"categories": [
{
"id": "pcat_pants",
"created_at": "2023-10-22T17:43:13.981Z",
"updated_at": "2023-10-22T17:43:13.981Z",
"name": "Pants",
"description": "",
"handle": "pants",
"is_active": true,
"is_internal": false,
"parent_category_id": null,
"rank": 0
}
]
}
]
To filter products based on attributes, use the attributes
search parameter:
/store/products?attributes[]=attr_val_01HE7NV34S6A6MR9ENT7RZF66Q&attributes[]=attr_val_01HE7NV34SKH8NBQFCMM48KMDW
Additionally, product store routes will include attribute_values
, which you can use to display attributes in your product listings.
Conclusion
In conclusion, this plugin simplifies the management of custom attributes in Medusa.js, providing a more efficient way to add, filter, and display attributes. Check out the documentation and support the project on its GitHub repository.