Create a metaobject definition and upsert entries
Model a size chart as a metaobject: create the definition with metaobjectDefinitionCreate, then create or update entries by handle with metaobjectUpsert.
- API
- Admin GraphQL API
- Operations
- metaobjectDefinitionCreate, metaobjectUpsert
- Access scopes
- write_metaobject_definitions, write_metaobjects
- Replaces (REST)
- None; metaobjects are GraphQL only
- API version
- 2026-10
- Last checked
In short
To store structured content such as size charts or FAQs, create a metaobject definition with metaobjectDefinitionCreate (type, fields, access, capabilities), then write entries with metaobjectUpsert. Upsert finds the entry by type and handle: it updates the entry when it exists and creates it when it doesn’t, so the same import can run again safely. Reference the entry from a product metafield to show it.
How to use it
- Send CreateSizeChartDefinition once per store. displayNameKey picks the field shown as the entry’s name in the admin.
- Turn on the publishable capability if entries should have a draft and active status, and set storefront access to PUBLIC_READ for headless storefronts.
- Send UpsertSizeChart for each chart with its type and handle. Field values are strings; the json field takes a JSON string.
- Link a chart to products with a metaobject_reference metafield, then read it in Liquid or the Storefront API.
Code
metaobject-definition-create.graphqlmutation CreateSizeChartDefinition($definition: MetaobjectDefinitionCreateInput!) {
metaobjectDefinitionCreate(definition: $definition) {
metaobjectDefinition {
id
type
name
displayNameKey
fieldDefinitions {
key
name
required
type {
name
}
}
}
userErrors {
code
field
message
}
}
}
metaobject-definition-create.variables.json{
"definition": {
"name": "Size chart",
"type": "size_chart",
"displayNameKey": "title",
"access": {
"storefront": "PUBLIC_READ"
},
"capabilities": {
"publishable": {
"enabled": true
}
},
"fieldDefinitions": [
{"key": "title", "name": "Title", "type": "single_line_text_field", "required": true},
{
"key": "unit",
"name": "Unit",
"type": "single_line_text_field",
"validations": [{"name": "choices", "value": "[\"cm\",\"in\"]"}]
},
{"key": "rows", "name": "Rows", "type": "json", "required": true}
]
}
}
metaobject-definition-create.response.json{
"data": {
"metaobjectDefinitionCreate": {
"metaobjectDefinition": {
"id": "gid://shopify/MetaobjectDefinition/1234567890",
"type": "size_chart",
"name": "Size chart",
"displayNameKey": "title",
"fieldDefinitions": [
{"key": "title", "name": "Title", "required": true, "type": {"name": "single_line_text_field"}},
{"key": "unit", "name": "Unit", "required": false, "type": {"name": "single_line_text_field"}},
{"key": "rows", "name": "Rows", "required": true, "type": {"name": "json"}}
]
},
"userErrors": []
}
}
}
metaobject-upsert.graphqlmutation UpsertSizeChart($handle: MetaobjectHandleInput!, $metaobject: MetaobjectUpsertInput!) {
metaobjectUpsert(handle: $handle, metaobject: $metaobject) {
metaobject {
id
handle
displayName
capabilities {
publishable {
status
}
}
fields {
key
value
}
}
userErrors {
code
field
message
}
}
}
metaobject-upsert.variables.json{
"handle": {
"type": "size_chart",
"handle": "shirts"
},
"metaobject": {
"fields": [
{"key": "title", "value": "Shirts"},
{"key": "unit", "value": "cm"},
{"key": "rows", "value": "[{\"size\":\"S\",\"chest\":96},{\"size\":\"M\",\"chest\":102},{\"size\":\"L\",\"chest\":108}]"}
],
"capabilities": {
"publishable": {
"status": "ACTIVE"
}
}
}
}
metaobject-upsert.response.json{
"data": {
"metaobjectUpsert": {
"metaobject": {
"id": "gid://shopify/Metaobject/1234567890",
"handle": "shirts",
"displayName": "Shirts",
"capabilities": {
"publishable": {
"status": "ACTIVE"
}
},
"fields": [
{"key": "title", "value": "Shirts"},
{"key": "unit", "value": "cm"},
{"key": "rows", "value": "[{\"size\":\"S\",\"chest\":96},{\"size\":\"M\",\"chest\":102},{\"size\":\"L\",\"chest\":108}]"}
]
},
"userErrors": []
}
}
}
Written by Lintel. MIT license: free to use in your own store and in client work.
Limits and edge cases
- The type can’t be changed after creation. Prefix it with $app: to reserve it for your app.
- With publishable on, an entry is only visible on the storefront when its status is ACTIVE.
- On update, the values you send overwrite the stored ones.
- Webhooks for metaobjects/create, update and delete need a filter naming the type, such as type:size_chart.
Questions
- What is the difference between metafields and metaobjects?
- A metafield adds one value to an existing resource. A metaobject is a resource of its own with several fields, like a size chart or an FAQ entry, that many products can reference.
- How do I update a metaobject without knowing its ID?
- Use metaobjectUpsert with the type and handle. It updates the entry if the handle exists and creates it otherwise.
- Why doesn’t my metaobject show on the storefront?
- Check that the definition has storefront access PUBLIC_READ and, if it is publishable, that the entry’s status is ACTIVE.
Shopify documentation
- Validated against the Admin or Storefront API schema for version 2026-10 on the date shown. Check the version in your request URL.
- IDs and store addresses in the examples are placeholders. Replace them with your own.
- Admin API calls need an access token from your app. Never put an Admin API token in a storefront or theme.
- MIT license. Use the code in your own store or in client work.
Need this built, tested and maintained for your store? See API integrations.
