> 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.
## 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 } } }"}'
```