How to Manage Products
In this document, you’ll learn how to manage products using the admin APIs.
Overview
Using the admin APIs, you can manage products, their options, and their variants.
Scenario
You want to add or use the following admin functionalities:
- Manage products. This includes listing, adding, updating, and deleting products.
- Manage product options. This includes adding, updating, or deleting options.
- Manage product variants. This includes listing, adding, updating, or deleting variants.
Prerequisites
Medusa Components
It is assumed that you already have a Medusa backend installed and set up. If not, you can follow the quickstart guide to get started.
JS Client
This guide includes code snippets to send requests to your Medusa backend using Medusa’s JS Client, among other methods.
If you follow the JS Client code blocks, it’s assumed you already have Medusa’s JS Client installed and have created an instance of the client.
Medusa React
This guide also includes code snippets to send requests to your Medusa backend using Medusa React, among other methods.
If you follow the Medusa React code blocks, it's assumed you already have Medusa React installed and have used MedusaProvider higher in your component tree.
Authenticated Admin User
You must be an authenticated admin user before following along with the steps in the tutorial.
You can learn more about authenticating as an admin user in the API reference.
List Products
You can list products as an admin using the List Products API Route:
import { useAdminProducts } from "medusa-react"
const Products = () => {
const { products, isLoading } = useAdminProducts()
return (
<div>
{isLoading && <span>Loading...</span>}
{products && !products.length && <span>No Products</span>}
{products && products.length > 0 && (
<ul>
{products.map((product) => (
<li key={product.id}>{product.title}</li>
))}
</ul>
)}
</div>
)
}
export default Products
This API Route doesn't require any parameters. You can pass parameters to search and filter through the retrieved products. For example, you can pass the q
parameter which searches through product titles, descriptions, and more. You can learn more about available parameters in the API reference.
You can also specify which fields to returns or which relations to expand using the fields
and expand
query parameters. You can learn more about them in the API reference.
The request returns an array of products along with pagination parameters.
Create a Product
You can create a product by sending a request to the Create a Product API Route:
medusa.admin.products.create({
title: "Shirt",
is_giftcard: false,
discountable: true,
options: [
{
title: "Color",
},
{
title: "Size",
},
],
variants: [
{
title: "White Small Shirt",
prices: [
{
amount: 1000,
currency_code,
},
],
options: [
{
value: "White",
},
{
value: "Small",
},
],
},
],
collection_id,
categories: [
{
id: categoryId,
},
],
type: {
value: typeValue,
},
tags: [
{
value: tagValue,
},
],
})
.then(({ product }) => {
console.log(product.id)
})
import { useAdminCreateProduct } from "medusa-react"
const CreateProduct = () => {
const createProduct = useAdminCreateProduct()
// ...
const handleCreate = () => {
createProduct.mutate({
title: "Shirt",
is_giftcard: false,
discountable: true,
options: [
{
title: "Color",
},
{
title: "Size",
},
],
variants: [
{
title: "White Small Shirt",
prices: [
{
amount: 1000,
currency_code,
},
],
options: [
{
value: "White",
},
{
value: "Small",
},
],
},
],
collection_id,
categories: [
{
id: categoryId,
},
],
type: {
value: typeValue,
},
tags: [
{
value: tagValue,
},
],
}, {
onSuccess: ({ product }) => {
console.log(product.id)
}
})
}
// ...
}
export default CreateProduct
fetch(`<BACKEND_URL>/admin/products`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: "Shirt",
options: [
{
title: "Color",
},
{
title: "Size",
},
],
variants: [
{
title: "White Small Shirt",
prices: [
{
amount: 1000,
currency_code,
},
],
options: [
{
value: "White",
},
{
value: "Small",
},
],
},
],
collection_id,
categories: [
{
id: categoryId,
},
],
type: {
value: typeValue,
},
tags: [
{
value: tagValue,
},
],
}),
})
.then((response) => response.json())
.then(({ product }) => {
console.log(product.id)
})
curl -L -X POST '<BACKEND_URL>/admin/products' \
-H 'x-medusa-access-token: <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"title": "Shirt 2",
"options": [
{
"title": "Color"
},
{
"title": "Size"
}
],
"variants": [
{
"title": "White Shirt",
"prices": [
{
"amount": 1000,
"currency_code": "USD"
}
],
"options": [
{
"value": "White"
},
{
"value": "Small"
}
]
}
],
"collection_id": "<COL_ID>",
"categories": [
{
"id": "<CAT_ID>"
}
],
"type": {
"value": "<TYPE_VAL>"
},
"tags": [
{
"value": "<TAG_VAL>"
}
]
}'
This API Route only requires the title
body parameter, which is the name of the product. Other parameters passed in the example are optional. They are:
is_giftcard
: a boolean value that determines whether a product is a gift card. By default, it’sfalse
.is_discountable
: a boolean value that determines whether discounts can be applied on the product. By default, it’strue
.options
: an array of objects, each object has the required propertytitle
. This only defines the option names and not their values, as their values are defined within the product variant. The array length must be the same as that of the values passed for each variant.variants
: an array of objects, each object has the required propertiestitle
andprices
:title
is a string indicating the name of the variant.prices
is an array of objects, each indicating the different prices of the product variant. You can specify prices for different contexts, such as a price for different currencies or regions. You can learn more about possible properties to pass in a price object in the API reference.options
is an array of objects, each object requires thevalue
property.value
is a string indicating the value of the option. Each object within this array is matched with the object of the product’soptions
array at the same index. So, in the example above, the “White” value is matched with the “Color” option, and the “Small” value is matched with the “Size” option.
collection_id
: an optional ID of the collection to associate the product with.categories
: an optional array of product category IDs to associate the product with.type
: an optional object that holds a value of the type to associate the product with. If you don’t pass an ID in the object, a new product type will be created. Otherwise, the product will be associated with an existing product type.tags
: an optional array of objects, each object requires thevalue
property. If you don’t pass an ID in an object, a new product tag will be created. Otherwise, the product will be associated with an existing product tag.
You can learn about other available parameters in the API reference.
The request returns the created product as an object.
Product Handle
If you don’t pass the handle
body parameter, the handle of the product will be automatically set to the slug version of the title
parameter.
Product Variant Prices
For currencies that are not zero-decimal, you must set the prices of product variants as the actual amount multiplied by a hundred.
So, in the example above, if the amount
is set to 1000
, it means the price is actually 10
.
You can learn more about pricing and zero-decimal currencies here.
Retrieve a Product
You can retrieve a single product as an admin by sending a request to the Get a Product API Route:
import { useAdminProduct } from "medusa-react"
type Props = {
productId: string
}
const Product = ({ productId }: Props) => {
const {
product,
isLoading,
} = useAdminProduct(productId)
return (
<div>
{isLoading && <span>Loading...</span>}
{product && <span>{product.title}</span>}
</div>
)
}
export default Product
This API Route requires the product ID to be passed as a path parameter.
The request returns the product as an object.
Update a Product
You can update a product by sending a request to the Update Product API Route:
import { useAdminUpdateProduct } from "medusa-react"
type Props = {
productId: string
}
const Product = ({ productId }: Props) => {
const updateProduct = useAdminUpdateProduct(
productId
)
// ...
const handleUpdate = (
title: string
) => {
updateProduct.mutate({
title,
}, {
onSuccess: ({ product }) => {
console.log(product.id)
}
})
}
// ...
}
export default Product
This API Route requires the product ID as a path parameter. In the request’s body, you can pass any of the product’s fields to update. In the example above, you update the title
of the product.
You can learn about other body parameters that you can pass in the API reference.
The request returns the updated product as an object.
Manage Product Options
In this section, you’ll learn how to manage the options of the product. Although you can already do that through the Create a Product and Update a Product API Routes explained above, you can also use API Routes specific to product options.
Retrieve Product Options
You can retrieve a product’s options by retrieving the product either using the List Products or the Retrieve a Product API Routes. The options are available under the options
property.
Add a Product Option
You can add a product option to a product by sending a request to the Add Product Option API Route:
import { useAdminCreateProductOption } from "medusa-react"
type Props = {
productId: string
}
const CreateProductOption = ({
productId
}: Props) => {
const createOption = useAdminCreateProductOption(
productId
)
// ...
const handleCreate = (
title: string
) => {
createOption.mutate({
title
}, {
onSuccess: ({ product }) => {
console.log(product.options)
}
})
}
// ...
}
export default CreateProductOption
This API Route requires the product’s ID as a path parameter. In the request body parameter, it requires passing the title
of the option.
The request returns the updated product as an option. You can view available options under the options
property of the product.
Update a Product Option
You can update a product option by sending a request to the Update Product Option API Route:
import { useAdminUpdateProductOption } from "medusa-react"
type Props = {
productId: string
optionId: string
}
const ProductOption = ({
productId,
optionId
}: Props) => {
const updateOption = useAdminUpdateProductOption(
productId
)
// ...
const handleUpdate = (
title: string
) => {
updateOption.mutate({
option_id: optionId,
title,
}, {
onSuccess: ({ product }) => {
console.log(product.options)
}
})
}
// ...
}
export default ProductOption
This API Route requires the product’s ID and the product option’s ID to be passed as a path parameter.
In the body parameters, you can update the title
field of the product option.
The request returns the updated product as an option. You can view available options under the options
property of the product.
Delete a Product Option
You can delete a product option by sending a request to the Delete Product Option API Route:
import { useAdminDeleteProductOption } from "medusa-react"
type Props = {
productId: string
optionId: string
}
const ProductOption = ({
productId,
optionId
}: Props) => {
const deleteOption = useAdminDeleteProductOption(
productId
)
// ...
const handleDelete = () => {
deleteOption.mutate(optionId, {
onSuccess: ({ option_id, object, deleted, product }) => {
console.log(product.options)
}
})
}
// ...
}
export default ProductOption
This API Route requires the product’s ID and the product option’s ID to be passed as a path parameter.
The request returns the following fields:
option_id
: The ID of the deleted product option.product
: The updated product object. You can access remaining product options underproduct.options
.object
: The type of object that was deleted. In this case, the value will beoption
.deleted
: A boolean value indicating whether the option was deleted.
Manage Product Variants
In this section, you’ll learn how to manage the variants of the product. Although you can already do that through the Create a Product and Update a Product API Routes explained above, you can also use API Routes specific to product variants.
Retrieve Product Variants
You can retrieve a product’s variants by retrieving the product either using the List Products or the Retrieve a Product API Routes. The product variants are available under the variants
property.
Create Product Variants
You can create a product variant by sending a request to the Create Product Variant API Route:
import { useAdminCreateVariant } from "medusa-react"
type CreateVariantData = {
title: string
prices: {
amount: number
currency_code: string
}[]
options: {
option_id: string
value: string
}[]
}
type Props = {
productId: string
}
const CreateProductVariant = ({ productId }: Props) => {
const createVariant = useAdminCreateVariant(
productId
)
// ...
const handleCreate = (
variantData: CreateVariantData
) => {
createVariant.mutate(variantData, {
onSuccess: ({ product }) => {
console.log(product.variants)
}
})
}
// ...
}
export default CreateProductVariant
fetch(`<BACKEND_URL>/admin/products/${productId}/variants`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: "White Shirt",
prices: [
{
amount: 1000,
currency_code: "USD",
},
],
options: [
{
option_id,
value: "White",
},
],
}),
})
.then((response) => response.json())
.then(({ product }) => {
console.log(product.id)
})
curl -L -X POST '<BACKEND_URL>/admin/products/<PRODUCT_ID>/variants' \
-H 'x-medusa-access-token: <API_TOKEN>' \
--header 'Content-Type: application/json' \
--data-raw '{
"title": "White Shirt",
"prices": [
{
"amount": 1000,
"currency_code": "USD"
}
],
"options": [
{
"option_id": "<OPT_ID>",
"value": "White"
}
]
}'
This API Route requires the ID of the product as a path parameter. In the request body parameters, the following parameters are required:
title
: A string indicating the title of the product variant.prices
: an array of objects, each object indicating the pricing of the variant in different contexts. For example, you can specify different prices for different currencies or regions. You can learn about other accepted properties within price objects in the API reference. You can also learn about formatting theamount
property in this section.options
: an array of objects, each object indicating a product option. Each object must have the following properties:option_id
: The ID of the product option.value
: A string indicating the value of the product option.
You can pass other optional parameters as well as indicated in the API reference.
The request returns the updated product. You can access the product’s variants under the variants
property of the product.
Update a Product Variant
You can update a product variant by sending a request to the Update a Product Variant API Route:
import { useAdminUpdateVariant } from "medusa-react"
type Props = {
productId: string
variantId: string
}
const ProductVariant = ({
productId,
variantId
}: Props) => {
const updateVariant = useAdminUpdateVariant(
productId
)
// ...
const handleUpdate = (title: string) => {
updateVariant.mutate({
variant_id: variantId,
title,
}, {
onSuccess: ({ product }) => {
console.log(product.variants)
}
})
}
// ...
}
export default ProductVariant
fetch(`<BACKEND_URL>/admin/products/${productId}/variants/${variantId}`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: "White Shirt",
}),
})
.then((response) => response.json())
.then(({ product }) => {
console.log(product.id)
})
This API Route requires the product ID and variant ID to be passed as a path parameter.
In the request body parameters, you can pass any of the variant’s attributes that you want to update. In the example above, you edit the title
of the variant. You can refer to the API reference for other possible parameters.
The request returns the updated product. You can access the product’s variants under the variants
property of the product.
Delete Product Variant
You can delete a product variant by sending a request to the Delete a Product Variant API Route:
import { useAdminDeleteVariant } from "medusa-react"
type Props = {
productId: string
variantId: string
}
const ProductVariant = ({
productId,
variantId
}: Props) => {
const deleteVariant = useAdminDeleteVariant(
productId
)
// ...
const handleDelete = () => {
deleteVariant.mutate(variantId, {
onSuccess: ({ variant_id, object, deleted, product }) => {
console.log(product.variants)
}
})
}
// ...
}
export default ProductVariant
This API Route requires the product ID and variant ID to be passed as a path parameter.
The request returns the following fields:
variant_id
: The ID of the deleted product variant.product
: The updated product object. You can access remaining product variants underproduct.variants
.object
: The type of object that was deleted. In this case, the value will beproduct-variant
.deleted
: A boolean value indicating whether the variant was deleted.
Delete a Product
You can delete a product by sending a request to the Delete a Product API Route:
import { useAdminDeleteProduct } from "medusa-react"
type Props = {
productId: string
}
const Product = ({ productId }: Props) => {
const deleteProduct = useAdminDeleteProduct(
productId
)
// ...
const handleDelete = () => {
deleteProduct.mutate(void 0, {
onSuccess: ({ id, object, deleted}) => {
console.log(id)
}
})
}
// ...
}
export default Product
This API Route requires passing the product ID as a path parameter.
The request returns the following fields:
id
: The ID of the deleted product.object
: The type of object that was deleted. In this case, the value will beproduct
.deleted
: A boolean value indicating whether the product was deleted.