Skip to main content
Beta

This feature is in beta and subject to change.

Declarative custom data definitions

Beta

The Next-Gen Dev Platform is not yet generally available. Currently, access to these tools and features is limited to organizations participating in an invitation-only early access program. To request entry into the early access program, submit a request.

Visit our dev community to discuss the Next-Gen Dev Platform and provide feedback.

Declarative custom data definitions make it easier to manage your metafields and metaobjects. Managing definitions via API is challenging for statically defined schemas:

  • Definitions are created during the application's runtime. That necessitates post-install hooks to check for and update existing definitions, with no rollback option in case of errors.

  • Schema migrations may require custom scripts to update each store individually.

  • Limits are scoped to the shop and shared among apps.

    Declarative custom data definitions address these considerations:

  • Metafield and metaobject definitions are organized in TOML and managed through the new Shopify app development command.

  • Shopify automatically distributes definitions to multiple shops in parallel, accelerating migrations.

  • Updates are atomic, ensuring all stores maintain the same version of your definitions.

  • All declarative definitions come with app-scoped limits, eliminating competition for resources among apps.

    If your use case requires dynamically creating definitions, we still recommend using the API. For all other scenarios, we recommend declarative custom data definitions.


In this scenario, you want to display custom payment messages in Checkout based on the selected payment method. This can be achieved by defining a metaobject for payment messages and associating it with payment types.

Anchor to Creating the metaobject definitionCreating the metaobject definition

First, declare the payment messages metaobject definition in your shopify.app.toml file:

[metaobjects.app.payment_messages]
name = "Payment Messages"

[metaobjects.app.payment_messages.fields.payment_type]
name = "Payment Type"
type = "single_line_text_field"
required = true

[metaobjects.app.payment_messages.fields.message]
name = "Message"
type = "single_line_text_field"
required = true

Anchor to Creating a metaobject entryCreating a metaobject entry

To create a payment message metaobject entry, use the GraphQL Admin API mutation metaobjectCreate:

mutation MetaobjectCreate {
metaobjectCreate(
metaobject: {
type: "$app:payment_messages"
fields: [
{ key: "payment_type", value: "creditCard" }
{ key: "message", value: "Pay securely with your credit card." }
]
}
) {
metaobject {
id
handle
type
fields {
key
value
}
}
userErrors {
field
message
}
}
}

Anchor to Retrieve your metaobject entryRetrieve your metaobject entry

You can retrieve your payment message metaobject entries using the metaobjects query:

query Metaobjects {
metaobjects(type: "$app:payment_messages", first: 10) {
edges {
node {
id
handle
fields {
key
value
}
}
}
}
}

Anchor to Adding validations to the metaobject definitionAdding validations to the metaobject definition

To restrict the allowed values for payment_type, add a validations.choices property to the field in your shopify.app.toml:

[metaobjects.app.payment_messages.fields.payment_type]
name = "Payment Type"
type = "single_line_text_field"
required = true
validations.choices = [
"creditCard", "manualPayment", "wallet", "paymentOnDelivery", "other"
]

Try to create a metaobject entry with an invalid payment_type value:

mutation MetaobjectCreate {
metaobjectCreate(
metaobject: {
type: "$app:payment_messages"
fields: [
{ key: "payment_type", value: "invalidType" }
{ key: "message", value: "This should fail validation." }
]
}
) {
metaobject {
id
handle
type
fields {
key
value
}
}
userErrors {
field
message
}
}
}

The response will include a userErrors entry indicating that the value for payment_type is not allowed.

Now, create a metaobject entry with a valid payment_type value:

mutation MetaobjectCreate {
metaobjectCreate(
metaobject: {
type: "$app:payment_messages"
fields: [
{ key: "payment_type", value: "creditCard" }
{ key: "message", value: "Pay securely with your credit card." }
]
}
) {
metaobject {
id
handle
type
fields {
key
value
}
}
userErrors {
field
message
}
}
}

This mutation will succeed and return the created metaobject entry, with an empty userErrors array.

Anchor to Modifying access controlsModifying access controls

You can control who can read or write to your metaobject definitions by adding access states to your TOML configuration. For example, to allow merchant read/write access in the admin, update your definition as follows:

[metaobjects.app.payment_messages]
name = "Payment Messages"
access.admin = "merchant_read_write"

After deploying your changes, you can see the updated access controls reflected in the Shopify Admin. Navigate to the Metaobjects tab within Content; you should see the payment_messages definition visible. This means the merchant can now modify entries on the definition.

Metaobjects in Shopify Admin.

shopify.app.toml

...

[metafields]
api_version = "2025-01"

# Product metafield
[product.metafields.app.page_count]
type = "number_integer"
name = "Page Count"
description = "Number of pages in the product"

access.admin = "merchant_read_write"
access.storefront = "public_read"
access.customer_account = "public_read"

capabilities.admin_filterable = true

validations.min = 10
validations.max = 1000


# Product metafields that references metaobjects
[product.metafields.app.writer]
type = "metaobject_reference<$app:author>"

[product.metafields.app.writer_or_publisher]
type = "mixed_reference<$app:author, $app:publisher>"


# "Author" Metaobject
[metaobjects.app.author]
name = "Author"
description = "Information about book authors"
display_name_field = "name"

access.admin = "merchant_read_write"
access.storefront = "public_read"

capabilities.publishable = true
capabilities.translatable = true

[metaobjects.app.author.fields.name]
type = "single_line_text_field"
name = "Author Name"
description = "Full name of the author"
required = true

[metaobjects.app.author.fields.bio]
type = "multi_line_text_field"
name = "Biography"
description = "Author biography"

[metaobjects.app.author.fields.books_written]
type = "number_integer"
name = "Books Written"
validations.min = 1


# "Publisher" Metaobject
[metaobjects.app.publisher]
name = "Publisher"
display_name_field = "name"

[metaobjects.app.publisher.fields.name]
type = "single_line_text_field"
name = "Publisher Name"

Anchor to Metafield configurationMetafield configuration

Metafields are declared using the format [<owner_type>.metafields.app.<key>]. For example, [product.metafields.app.page_count] declares a Product metafield, with namespace $app and key page_count.

Metafields can be defined on many different resource types (e.g. product). For a full list, see MetafieldOwnerType.

PropertyDescription
typeData type for the metafield. See metafield data types for all available options.
nameHuman-readable name displayed in the Shopify admin.
descriptionDescriptive text that explains the purpose of the metafield.
access.adminAdmin API access control: merchant_read or merchant_read_write.
access.storefrontStorefront access control: public_read, private_read, or none.
access.customer_accountCustomer API access control: public_read, public_read_write, private_read, private_read_write, or none.
capabilities.admin_filterableWhen true, enables filtering by this metafield in the admin UI & admin API.
validationsRules to validate field values (e.g., min/max values, regex patterns). See validation rules.

Anchor to Metaobject configurationMetaobject configuration

Metaobjects are declared using the format [metaobjects.app.<metaobject_name />]. For example, [metaobjects.app.author] declares a metaobject named author, with type name $app:author.

PropertyDescription
nameHuman-readable name displayed in the Shopify admin.
descriptionDescriptive text that explains the purpose of the metaobject.
display_name_fieldKey of a field to reference as the display name for each object.
access.adminAdmin API access control: merchant_read or merchant_read_write.
access.storefrontStorefront access control: public_read, private_read, or none.
capabilities.publishableWhen true, enables draft/active status for content workflow. See metaobject capabilities.
capabilities.translatableWhen true, enables translation support for fields. See translatable content.
capabilities.renderableWhen true, enables metaobject SEO fields on Liquid and the Storefront API. See metaobjects as web pages.

Anchor to Metaobject fields configurationMetaobject fields configuration

Metaobject fields are declared using the format [metaobjects.app.<metaobject_name />.fields.<field_name>]. For example, [metaobjects.app.author.fields.birthday] declares a field named birthday on the author metafield.

PropertyDescription
typeData type for the field. Uses the same types as metafields. See metafield data types.
nameHuman-readable name displayed in the Shopify admin.
descriptionDescriptive text that explains the purpose of the field.
requiredWhen true, the field must have a value when saving the metaobject.
validationsRules to validate field values based on the field type. See validation rules.

Anchor to App-reserved namespaceApp-reserved namespace

You can only declare metafield and metaobject definitions in the app-reserved namespace ($app) to ensure that only the owning app can make changes to definitions. This constraint allows Shopify to guarantee a consistent state between all shops your app is installed on.

Anchor to App-scoped metafield and metaobject limitsApp-scoped metafield and metaobject limits

There are limits on the total number of definitions within an app.

  • Metafield definitions per owner type: 128

  • Metaobject definitions: 32

  • Field definitions per metaobject: 64

    To ensure Shopify can quickly and reliably distribute definitions across shops, you can't make more than 25 metafield and metaobject changes (creation, update or deletion) in a single deploy. If you need to make more than 25 changes, you will need to do so over multiple deploys.

Declarative definitions are read-only through the Admin API, and can only be updated or deleted through the TOML configuration file. You can query declarative definitions through the Admin API, but mutations will return an error.

Support for currently unsupported capabilities is coming later in 2025.

CapabilitySupported?
Publishable
Translatable
Renderable
Online Store

Anchor to Making GraphQL calls with AI IDEsMaking GraphQL calls with AI IDEs

When the user is running the dev server, they will also be running a GQL proxy server on http://localhost:3457/graphiql/graphql.json?key=&api_version=2025-07 where you can POST graphql queries and they will be run against their dev store.

curl -X POST 'http://localhost:3457/graphiql/graphql.json?key=&api_version=2025-04' -H 'Content-Type: application/json' --data-binary '{"query":"mutation MetaobjectCreate { metaobjectCreate(metaobject: { type: \"$app:product_highlight\", fields: [ { key: \"title\", value: \"Summer Sale\" }, { key: \"description\", value: \"Highlighting our best summer products!\" } ] }) { metaobject { id handle type fields { key value } } userErrors { field message } } }"}'

Was this page helpful?