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

  1. Send CreateSizeChartDefinition once per store. displayNameKey picks the field shown as the entry’s name in the admin.
  2. Turn on the publishable capability if entries should have a draft and active status, and set storefront access to PUBLIC_READ for headless storefronts.
  3. Send UpsertSizeChart for each chart with its type and handle. Field values are strings; the json field takes a JSON string.
  4. Link a chart to products with a metaobject_reference metafield, then read it in Liquid or the Storefront API.

Code

metaobject-definition-create.graphql
mutation 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.graphql
mutation 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.
  • 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.

Have something to build? Let’s talk it through.

Taking a limited number of projects at a time. Every brief is read by an engineer, not a salesperson.

Gencer KarakayaShopify Engineer

An engineer reads your brief, and we reply by email.