# 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.

Page: https://lintelagency.com/resources/graphql/metaobject-definition-and-upsert

## 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.

## Facts

- **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:** 2 November 2026

## 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

```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

```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

```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

```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

```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

```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": []
    }
  }
}
```

## 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

- [metaobjectDefinitionCreate](https://shopify.dev/docs/api/admin-graphql/latest/mutations/metaobjectDefinitionCreate)
- [metaobjectUpsert](https://shopify.dev/docs/api/admin-graphql/latest/mutations/metaobjectUpsert)
- [Manage metaobjects](https://shopify.dev/docs/apps/build/custom-data/metaobjects/manage-metaobjects)

## Related

- [Read metafields and metaobjects with the Storefront API](https://lintelagency.com/resources/graphql/storefront-metafields-and-metaobjects)
- [Create a metafield definition](https://lintelagency.com/resources/graphql/create-metafield-definition)
- [Hydrogen vs Liquid: when headless pays for itself](https://lintelagency.com/journal/shopify-hydrogen-vs-liquid)

License: MIT. Free to use in your own store and in client work.
