Skip to main content

Edit product data using the new product model

Legacy

The REST Admin API is a legacy API as of October 1, 2024. All apps and integrations should be built with the GraphQL Admin API. For details and migration steps, visit our migration guide.

Learn how to use the new product model to edit the product, variant, and option data that you created.


In this guide, you'll learn how to update data for an existing product either by overwriting the data in a single mutation, or by updating its individual fields incrementally through multiple mutations.

You'll also learn the workflow for updating a product and its variants and operations using updated product operations.

Note

If your app or use case aligns with the database sync workflow, then use the productSet mutation to edit data in a single operation.


You've created a new product with variants and options. Now you want to make the following changes:

  • Change the title from My Cool Socks to My Extra Cool Socks.

  • Get rid of sizing, and switch to a one-size-fits-all model.

  • Add a new Material option with values of Cotton and Wool, listing all existing socks as Cotton.

  • Rename the Material option to Fabric.

  • Swap Color and Material’s positions to make Material options and variants display first in the product.

  • Remove two color options.

  • Add two new colors of cotton socks.

  • Add five new colors of wool socks.


Anchor to Shape of the product dataShape of the product data

The following diagram represents the original shape of the product data:

The relationship between option value, variant, option, and values, which comprise data for a product

The diagram displays the "OptionValues" that are assigned to each variant, and the "Values" that belong to each "Option" relationship of the product's variants to their option values. On the right, there's the relationship of the product's options to their option values. The values that are represented in "OptionValues" correspond with a value in the product's options.

The following is the corresponding data structure:

Product data structure

{
"id": "gid://shopify/Product/456",
"title": "My Cool Socks",
"variants": {
"edges": [
{
"node": {
"id": "gid://shopify/ProductVariant/20",
"title": "Red / Small",
"selectedOptions": [
{
"name": "Color",
"value": "Red"
},
{
"name": "Size",
"value": "Small"
}
]
}
},
{
"node": {
"id": "gid://shopify/ProductVariant/21",
"title": "Green / Small",
"selectedOptions": [
{
"name": "Color",
"value": "Green"
},
{
"name": "Size",
"value": "Small"
}
]
}

Anchor to Several mutations to update the product dataSeveral mutations to update the product data

The following mutations should be used when Shopify is the system for managing product domain information. The mutations are designed for smaller changes per request. Consequently, the valid calls are easier to construct and their individual execution duration is shorter.

Anchor to Update the product titleUpdate the product title

Update the product title with the productUpdate mutation. This example updates the product title from My Cool Socks to My Extra Cool Socks:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation UpdateProduct {
productUpdate(input: {
id: "gid://shopify/Product/456",
title: "My Extra Cool Socks"
}) {
product {
title
}
userErrors {
field
message
}
}
}

JSON response

{
"data": {
"productUpdate": {
"product": {
"title": "My Extra Cool Socks"
},
"userErrors": []
}
}
}

The following diagram illustrates the product data after the update:

The product

Anchor to Remove the Size optionRemove the Size option

Because you're moving the product to a one-size-fits-all model, you can remove the Size option.

Remove an option with the productOptionsDelete mutation, passing the the ID of the Size option. However, the product is already offered in multiple sizes, so you need to decide which variants no longer need to be offered.

You can delete unneeded variants in the following ways:

  • Delete the variants that you don’t want to preserve, until each product variant remaining represents a unique Color. Update the remaining variants to all have one Size. Then, delete the Size option.

    You might want to delete variants manually if you need fine-grained control over which variants you want to delete, For example, to preserve variants with specific SKUs or prices to be preserved.

    Tip

    When there are more than two options, delete variants until each product variant represents a unique option combination.

  • Use the strategy argument with a value of POSITION. This strategy automatically resolves duplicate option combinations for you, by deleting any duplicated option combinations by position (descending).

    This behavior uses the same strategy that the Shopify admin uses to automatically delete any product variants that would be a duplicate of another variant after a product option is deleted. The POSITION strategy enables you to implement the same approach in your app with minimal effort.

    Tip

    When you don't care which of the variants is deleted, use this option. This helps avoid round trip calls to the API.

The following example deletes the Size option using strategy: POSITION:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation DeleteOptionsFromProduct {
productOptionsDelete(
productId: "gid://shopify/Product/456",
options: ["gid://shopify/ProductOption/102"],
strategy: POSITION
) {
product {
options {
id
name
position
optionValues {
id
name
hasVariants
}
}
}
userErrors {
field
message
}
}
}

JSON response

{
"data": {
"productOptionsDelete": {
"product": {
"options": [
{
"id": "gid://shopify/ProductOption/101",
"name": "Color",
"position": 1,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/1",
"name": "Red",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/2",
"name": "Green",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/3",
"name": "Blue",
"hasVariants": true
}
]
}
]
},
"userErrors": []
}
}
}

After the product option and its variants are deleted, the product has the following structure:

The product data with the
Note

Consider using the NON_DESTRUCTIVE strategy when all option combinations, excluding Size, would be unique after Size is removed. This strategy automatically updates the remaining variants, removing the Size option and ensuring a unique set of option combinations.

Anchor to Add the Material optionAdd the Material option

Add a new Material option with the productOptionsCreate mutation. When you run this mutation, all existing variants are backfilled to use the first specified option value for the new option. In this example, the option is Cotton.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation CreateProductOption($productId: ID!, $options: [OptionCreateInput!]!) {
productOptionsCreate(productId: $productId, options: $options) {
product {
options {
id
name
position
optionValues {
id
name
hasVariants
}
}
variants(first: 250) {
edges {
node {
id
title
selectedOptions {
name
value
}
}
}
}
}
userErrors {
field
message
}
}
}

Arguments

{
"productId": "gid://shopify/Product/456",
"options": [
{
"name": "Material",
"values": [
{ "name": "Cotton" },
{ "name": "Wool" }
]
}
]
}

JSON response

{
"data": {
"productOptionsCreate": {
"product": {
"options": [
{
"id": "gid://shopify/ProductOption/101",
"name": "Color",
"position": 1,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/1",
"name": "Red",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/2",
"name": "Green",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/3",
"name": "Blue",
"hasVariants": true
}
]
},
{
"id": "gid://shopify/ProductOption/103",
"name": "Material",
"position": 2,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/7",
"name": "Cotton",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/8",
"name": "Wool",
"hasVariants": false
}
]
}
],
"variants": {
"edges": [
{
"node": {
"id": "gid://shopify/ProductVariant/101",
"title": "Red / Cotton",
"selectedOptions": [
{
"name": "Color",
"value": "Red"
},
{
"name": "Material",
"value": "Cotton"
}
]
}
},
{
"node": {
"id": "gid://shopify/ProductVariant/102",
"title": "Green / Cotton",
"selectedOptions": [
{
"name": "Color",
"value": "Green"
},
{
"name": "Material",
"value": "Cotton"
}
]
}
},
{
"node": {
"id": "gid://shopify/ProductVariant/103",
"title": "Blue / Cotton",
"selectedOptions": [
{
"name": "Color",
"value": "Blue"
},
{
"name": "Material",
"value": "Cotton"
}
]
}
}
]
}
},
"userErrors": []
}
}
}

After the new product option is added, the product has the following structure:

The product data with the

Anchor to Update the Material optionUpdate the Material option

You can also use the productOptionUpdate mutation to rename a product option or reorder the options. This example renames Material to Fabric and changes its position to display first in the product:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation UpdateOptionNameAndPosition($productId: ID!, $optionInput: OptionUpdateInput!) {
productOptionUpdate(productId: $productId, option: $optionInput) {
product {
options {
id
name
position
optionValues {
id
name
hasVariants
}
}
}
userErrors {
field
message
}
}
}

Arguments

{
"productId": "gid://shopify/Product/456",
"optionInput": {
"id": "gid://shopify/ProductOption/102",
"name": "Fabric",
"position": 1
}
}

JSON response

{
"data": {
"productOptionUpdate": {
"product": {
"options": [
{
"id": "gid://shopify/ProductOption/103",
"name": "Fabric",
"position": 1,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/7",
"name": "Cotton",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/8",
"name": "Wool",
"hasVariants": false
}
]
},
{
"id": "gid://shopify/ProductOption/101",
"name": "Color",
"position": 2,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/1",
"name": "Red",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/2",
"name": "Green",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/3",
"name": "Blue",
"hasVariants": true
}
]
}
]
},
"userErrors": []
}
}
}

After the option is renamed and the options are reordered, your product has the following structure:

The product data with the

Anchor to Update variant option values by IDUpdate variant option values by ID

Each time you query a product option's optionValues, both Wool and Cotton are returned.

Because Cotton was backfilled for existing variants, its hasVariants value is true, where Wool's hasVariants value is false. Option values with a hasVariants value of false are referred to as orphaned option values.

Tip

Orphaned option values still have a GID associated with them. In subsequent requests, you can reference orphaned option values by ID, such as for data integrity purposes, local storage, and reflection in the UI.

The following example uses the productVariantsBulkUpdate mutation to update the Green color sock to be Wool. The option value is referenced by its id.

Tip

You can also reference your product options or option values by name.

For example, optionValues: { "name": "Wool", "optionName": "Fabric" }

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation UpdateVariantOptionValueByReference($productId: ID!, $variantsInput: [ProductVariantsBulkInput!]!) {
productVariantsBulkUpdate(productId: $productId, variants: $variantsInput) {
product {
options {
id
name
position
optionValues {
id
name
hasVariants
}
}
}
productVariants {
id
title
selectedOptions {
name
value
}
}
userErrors {
field
message
}
}
}

Arguments

{
"productId": "gid://shopify/Product/456",
"variantsInput": [
{
"id": "gid://shopify/ProductVariant/102",
"optionValues": [
{
"optionId": "gid://shopify/ProductOption/3"
"id": "gid://shopify/ProductOptionValue/8",
}
]
}
]
}

JSON response

{
"data": {
"productVariantsBulkUpdate": {
"product": {
"options": [
{
"id": "gid://shopify/ProductOption/3",
"name": "Fabric",
"position": 1,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/7",
"name": "Cotton",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/8",
"name": "Wool",
"hasVariants": true
}
]
},
{
"id": "gid://shopify/ProductOption/1",
"name": "Color",
"position": 2,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/1",
"name": "Red",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/2",
"name": "Green",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/3",
"name": "Blue",
"hasVariants": true
}
]
}
]
},
"productVariants": [
{
"id": "gid://shopify/ProductVariant/102",
"title": "Wool / Green",
"selectedOptions": [
{
"name": "Fabric",
"value": "Wool"
},
{
"name": "Color",
"value": "Green"
}
]
}
],
"userErrors": []
}
}
}

In the response, Wool’s hasVariants value is now true because the option is no longer orphaned.

After the Wool option value is assigned to a variant, your product has the following structure:

The product data with the

Use the productOptionUpdate mutation to set the new position of each individual option manually. However, if you only need to reorder options and option values, or need to reorder multiple options and option values, then you might want to use the productOptionsReorder instead.

The following example switches the order of all of the options and option values, displays Color first with a value order of Blue, Red, Green, and then displays Fabric with a value order of Wool, Cotton:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation ReorderOptionsAndVariants($productId: ID!, $optionsInput: [OptionReorderInput!]!) {
productOptionsReorder(productId: $productId, options: $optionsInput) {
product {
options {
id
name
position
optionValues {
id
name
hasVariants
}
}
}
userErrors {
field
message
}
}
}

Arguments

{
"productId": "gid://shopify/Product/456",
"optionsInput": [
{
"name": "Color",
"values": [
{ "name": "Blue" },
{ "name": "Red" },
{ "name": "Green" }
]
},
{
"name": "Fabric",
"values": [
{ "name": "Wool" },
{ "name": "Cotton" }
]
}
]
}

JSON response

{
"data": {
"productOptionsReorder": {
"product": {
"options": [
{
"id": "gid://shopify/ProductOption/1",
"name": "Color",
"position": 1,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/3",
"name": "Blue",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/1",
"name": "Red",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/2",
"name": "Green",
"hasVariants": true
}
]
},
{
"id": "gid://shopify/ProductOption/3",
"name": "Fabric",
"position": 2,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/7",
"name": "Cotton",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/8",
"name": "Wool",
"hasVariants": true
}
]
}
]
},
"userErrors": []
}
}
}

After the options and option values are reordered, your product has the following structure:

The product data with

The following example uses the productVariantsBulkDelete mutation to remove the variants that use the Red and Green color options:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation DeleteVariants($productId: ID!, $variantsToDelete: [ID!]!) {
productVariantsBulkDelete(productId: $productId, variantsIds: $variantsToDelete) {
product {
id
options {
id
name
position
optionValues {
id
name
hasVariants
}
}
variants(first: 5) {
nodes {
id
title
selectedOptions {
name
value
}
}
}
}
userErrors {
field
message
code
}
}
}

Arguments

{
"productId": "gid://shopify/Product/456",
"variantsToDelete": [
"gid://shopify/ProductVariant/20",
"gid://shopify/ProductVariant/21"
]
}

JSON response

{
"data": {
"productVariantsBulkDelete": {
"product": {
"id": "gid://shopify/Product/456",
"options": [
{
"id": "gid://shopify/ProductOption/1",
"name": "Color",
"position": 1,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/3",
"name": "Blue",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/1",
"name": "Red",
"hasVariants": false
},
{
"id": "gid://shopify/ProductOptionValue/2",
"name": "Green",
"hasVariants": false
}
]
},
{
"id": "gid://shopify/ProductOption/3",
"name": "Fabric",
"position": 2,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/1",
"name": "Cotton",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/2",
"name": "Wool",
"hasVariants": false
}
]
}
],
"variants": {
"nodes": [
{
"id": "gid://shopify/ProductVariant/103",
"title": "Blue / Cotton",
"selectedOptions": [
{
"name": "Color",
"value": "Blue"
},
{
"name": "Fabric",
"value": "Cotton"
}
]
}
]
}
},
"userErrors": []
}
}
}

The option values for which all variants were deleted are preserved as orphaned option values.

After the variants are removed, your product has the following structure:

The product data, with the

Anchor to Tip: Clean up unused option valuesTip: Clean up unused option values

You can preserve unused option values to reference by ID or name at a later point. Alternatively, you can remove the values from the option using the productOptionUpdate mutation. The following is an example:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation UpdateOptionToDeleteOrphanedOptionValues($productId: ID!, $option: OptionUpdateInput!, $optionValuesToDelete: [ID!]) {
productOptionUpdate(productId: $productId, option: $option, optionValuesToDelete: $optionValuesToDelete) {
product {
id
options {
id
name
position
optionValues {
id
name
hasVariants
}
}
variants(first: 5) {
nodes {
id
title
selectedOptions {
name
value
}
}
}
}
userErrors {
field
message
code
}
}
}

Arguments

{
"productId": "gid://shopify/Product/456",
"option": {
"id": "gid://shopify/ProductOption/100",
},
"optionValuesToDelete": [
"gid://shopify/ProductOptionValue/10079785100",
"gid://shopify/ProductOptionValue/10079785101"
]
}

JSON response

{
"data": {
"productOptionUpdate": {
"product": {
"id": "gid://shopify/Product/456",
"options": [
{
"id": "gid://shopify/ProductOption/1",
"name": "Color",
"position": 1,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/3",
"name": "Blue",
"hasVariants": true
}
]
},
{
"id": "gid://shopify/ProductOption/3",
"name": "Fabric",
"position": 2,
"optionValues": [
{
"id": "gid://shopify/ProductOptionValue/7",
"name": "Cotton",
"hasVariants": true
},
{
"id": "gid://shopify/ProductOptionValue/8",
"name": "Wool",
"hasVariants": false
}
]
}
],
"variants": {
"nodes": [
{
"id": "gid://shopify/ProductVariant/103",
"title": "Blue / Cotton",
"selectedOptions": [
{
"name": "Color",
"value": "Blue"
},
{
"name": "Fabric",
"value": "Cotton"
}
]
}
]
}
},
"userErrors": []
}
}
}

This example uses the productVariantsBulkCreate mutation to add the following new variants:

  • Cotton socks available in yellow and orange

  • Wool socks of different colors

    For the mix of existing and new variants, new colors are applied using a reference value or ID. Any new values are added as option values automatically.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation CreateProductVariants($productId: ID!, $variantsToCreate: [ProductVariantsBulkInput!]!) {
productVariantsBulkCreate(productId: $productId, variants: $variantsToCreate) {
product {
id
variants(first: 250) {
edges {
node {
id
title
selectedOptions {
name
value
}
}
}
}
}
userErrors {
field
message
}
}
}

Arguments

{
"productId": "gid://shopify/Product/456",
"variantsToCreate": [
{
"optionValues": [
{ "name": "Yellow", "optionName": "Color" },
{ "name": "Cotton", "optionName": "Fabric" }
]
},
{
"optionValues": [
{ "name": "Orange", "optionName": "Color" },
{ "name": "Cotton", "optionName": "Fabric" }
]
},
{
"optionValues": [
{ "name": "Turquoise", "optionName": "Color" },
{ "name": "Wool", "optionName": "Fabric" }
]
},
{
"optionValues": [
{ "name": "Blue", "optionName": "Color" },
{ "name": "Wool", "optionName": "Fabric" }
]
},
{
"optionValues": [
{ "name": "Red", "optionName": "Color" },
{ "name": "Wool", "optionName": "Fabric" }
]
},
{
"optionValues": [
{ "name": "Green", "optionName": "Color" },
{ "name": "Wool", "optionName": "Fabric" }
]
},
{
"optionValues": [
{ "name": "Orange", "optionName": "Color" },
{ "name": "Wool", "optionName": "Fabric" }
]
},
{
"optionValues": [
{ "name": "Yellow", "optionName": "Color" },
{ "name": "Wool", "optionName": "Fabric" }
]
}
]
}

JSON response

{
"data": {
"productVariantsBulkCreate": {
"product": {
"id": "gid://shopify/Product/456",
"variants": {
"edges": [
{
"node": {
"id": "gid://shopify/ProductVariant/103",
"title": "Blue / Cotton",
"selectedOptions": [
{
"name": "Color",
"value": "Blue"
},
{
"name": "Fabric",
"value": "Cotton"
}
]
}
},
{
"node": {
"id": "gid://shopify/ProductVariant/110",
"title": "Yellow / Cotton",
"selectedOptions": [
{
"name": "Color",
"value": "Yellow"
},
{
"name": "Fabric",
"value": "Cotton"
}

After the new variants are added, your product has the following structure:

The product data with new variants that include wool and several new colors

Learn more about how to migrate from GraphQL to REST with the following resources:

Sync data

Learn how you can sync product data from an external source into Shopify.

Product management in GraphQL versus REST

Learn about the differences between using GraphQL and REST to interact with products, variants, and related API components.

Migrate to GraphQL

If you're new to using GraphQL at Shopify, review guides and resources for migrating your app to the GraphQL Admin API from the REST Admin API.

GraphQL basics

Learn about the basics of GraphQL, including its benefits over REST.


Was this page helpful?