> 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](https://forms.gle/8TYFJFs6obUk426V9). > Visit our [dev community](https://community.shopify.dev) 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](/beta/next-gen-dev-platform/shopify-app-dev). * 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. ## Example 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. ### Creating the metaobject definition First, declare the payment messages metaobject definition in your `shopify.app.toml` file: ```toml [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 ``` ### Creating a metaobject entry To create a payment message metaobject entry, use the GraphQL Admin API mutation `metaobjectCreate`: ```graphql 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 } } } ``` ### Retrieve your metaobject entry You can retrieve your payment message metaobject entries using the `metaobjects` query: ```graphql query Metaobjects { metaobjects(type: "$app:payment_messages", first: 10) { edges { node { id handle fields { key value } } } } } ``` ### Adding 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`: ```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: ```graphql 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: ```graphql 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. ### Modifying 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: ```toml [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.
## Reference ```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" ``` ### Metafield configuration Metafields are declared using the format `[.metafields.app.]`. 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](/docs/api/admin-graphql/latest/enums/MetafieldOwnerType#validvalues). | Property | Description | |----------|-------------| | `type` | Data type for the metafield. See [metafield data types](/docs/apps/build/custom-data/metafields/list-of-data-types) for all available options. | | `name` | Human-readable name displayed in the Shopify admin. | | `description` | Descriptive text that explains the purpose of the metafield. | | `access.admin` | Admin API access control: `merchant_read` or `merchant_read_write`. | | `access.storefront` | Storefront access control: `public_read`, `private_read`, or `none`. | | `access.customer_account` | Customer API access control: `public_read`, `public_read_write`, `private_read`, `private_read_write`, or `none`. | | `capabilities.admin_filterable` | When `true`, enables filtering by this metafield in admin UI. | | `validations` | Rules to validate field values (e.g., min/max values, regex patterns). See [validation rules](/docs/apps/build/custom-data/metafields/list-of-validation-options). | ### Metaobject configuration Metaobjects are declared using the format `[metaobjects.app.]`. For example, `[metaobjects.app.author]` declares a metaobject named `author`, with type name `$app:author`. | Property | Description | |----------|-------------| | `name` | Human-readable name displayed in the Shopify admin. | | `description` | Descriptive text that explains the purpose of the metaobject. | | `display_name_field` | Key of a field to reference as the display name for each object. | | `access.admin` | Admin API access control: `merchant_read` or `merchant_read_write`. | | `access.storefront` | Storefront access control: `public_read`, `private_read`, or `none`. | | `capabilities.publishable` | When `true`, enables draft/active status for content workflow. See [metaobject capabilities](/docs/apps/build/custom-data/metaobjects/use-metaobject-capabilities). | | `capabilities.translatable` | When `true`, enables translation support for fields. See [translatable content](/docs/apps/build/custom-data/metaobjects/use-metaobject-capabilities#make-your-metaobjects-translatable). | ### Metaobject fields configuration Metaobject fields are declared using the format `[metaobjects.app..fields.]`. For example, `[metaobjects.app.author.fields.birthday]` declares a field named `birthday` on the `author` metafield. | Property | Description | |----------|-------------| | `type` | Data type for the field. Uses the same types as metafields. See [metafield data types](/docs/apps/build/custom-data/metafields/list-of-data-types). | | `name` | Human-readable name displayed in the Shopify admin. | | `description` | Descriptive text that explains the purpose of the field. | | `required` | When `true`, the field must have a value when saving the metaobject. | | `validations` | Rules to validate field values based on the field type. See [validation rules](/docs/apps/build/custom-data/metafields/list-of-validation-options). | ## Limitations ### App-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. ### App-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. ### Admin API 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. ### Capability support Support for currently unsupported capabilities is coming later in 2025. #### Metafields | Capability | Supported? | |---|---| | [Smart collections](/docs/apps/build/custom-data/metafields/use-metafield-capabilities#smart-collection) | ❌ | | [Admin filterable](/docs/apps/build/custom-data/metafields/use-metafield-capabilities#admin-filterable) | ❌ | | [Unique values](/docs/apps/build/custom-data/metafields/use-metafield-capabilities#unique-values) | ❌ | | [Pinning](https://help.shopify.com/en/manual/custom-data/metafields/pinning-metafield-definitions) | ❌ | #### Metaobject | Capability | Supported? | |---|---| | [Publishable](/docs/apps/build/custom-data/metaobjects/use-metaobject-capabilities#draft-custom-content) | ✅ | | [Translatable](/docs/apps/build/custom-data/metaobjects/use-metaobject-capabilities#make-your-metaobjects-translatable) | ✅ | | [Renderable](/docs/apps/build/custom-data/metaobjects/use-metaobject-capabilities#render-metaobjects-as-web-pages) | ✅ | | [Online Store](/docs/apps/build/custom-data/metaobjects/use-metaobject-capabilities#make-your-metaobjects-render-web-pages-in-the-online-store) | ❌ | ## Making 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 } } }"}' ```