App-owned metafields and metaobjects not accessible to other apps via Storefront API

I’m building an app feature that requires app-owned metaobjects linked to products via a list.metaobject_reference metafield. Both are declared in shopify.app.toml with access.storefront = "public_read".

Setup:

[metaobjects.app.my_custom_type]
name = "My Custom Type"
access.storefront = "public_read"

[product.metafields.app.my_reference]
name = "My Reference"
type = "list.metaobject_reference<$app:my_custom_type>"
access.storefront = "public_read"


Our app can read the data fine via Storefront API using our own storefront access token — the metafield value and referenced metaobject entries resolve correctly.

Our requirement: Our architecture depends on third-party apps and partners being able to access these app-owned metafields and metaobjects via the Storefront API. The public_read access setting implies this should be possible, and we’ve confirmed it works using the resolved namespace.

The problem: When a third-party app or partner queries the same product via the Storefront API using their own storefront access token, the metafield references come back as null:

{
  "data": {
    "product": {
      "metafield": {
        "references": null
      }
    }
  }
}


What worked: When third-party apps and partners query our metafields using the $app namespace, the references return null. However, when they use the resolved namespace app–<APP_ID>, both the metafield value and the referenced metaobject entries resolve correctly.

query {
    product(id: "gid://shopify/Product/<<PRODUCT_ID>>") {
      customBadge: metafield(namespace: "app-<<APP_ID>>", key: "custom_badge") {
        namespace
        key
        type
        value
      }
    }
  }

Our question: Before we commit to this architecture, we want to confirm—is using the resolved namespace app–<APP_ID> the expected and supported approach for third-party apps and partners to access app-owned metafields and metaobject references via the Storefront API? Can this be relied on as a stable pattern going forward?

Since the changelog says otherwise.
https://shopify.dev/changelog/public-access-for-app-owned-metafields-and-metaobjects-is-now-disabled

We are still able to create app owned metaobject/metafield definitions through TOML and the GraphQL admin API with PUBLIC_READ storefront access.

Could someone help me with this.

@Liam-Shopify

Hey @Manoj_Putchala, thanks for digging into this and laying it out so clearly.

Short version: no, I wouldn’t recommend building on top of the resolved app--<APP_ID> namespace for third-party Storefront API access. It’s an internal storage detail, not a public contract — the fact that it currently resolves is incidental, and the changelog you referenced is the source of truth on direction of travel here.

The app reserved namespace ($app / app--<APP_ID>) is designed to be exclusive to the owning app — that’s the whole point of reserved namespaces. From the ownership docs: apps that need to share data with other apps should use a custom (merchant-owned) namespace, not a reserved one. Merchant-owned namespaces (custom, or any non-app--/shopify-- namespace) are explicitly intended for cross-app interop and are the supported path for what you’re describing.

That you can still set access.storefront = "public_read" on an app-owned metafield in TOML doesn’t mean third-party clients are guaranteed to be able to resolve it — the TOML accepts the config, but the platform behaviour is what the changelog describes.

What I’d recommend

If third-party apps and partners need to read this data via Storefront API, move the definitions to a custom namespace, e.g.:

[[metaobjects]]
type = "my_custom_type"
name = "My Custom Type"
[metaobjects.access]
storefront = "public_read"

[[product.metafields]]
namespace = "custom"
key = "my_reference"
type = "list.metaobject_reference<my_custom_type>"
[product.metafields.access]
storefront = "public_read"

Your app retains write control via the Admin API, but reads are no longer gated by the reserved-namespace rules.

Hey Liam,

This does still leave a gap of “I want to control what happens with this meta object as I’m using it like a database table and I want no one else to be able to write but I want everyone else to read”.

If we go with customer (merchant-owned) meta fields it doesn’t stop them going to Content → Metaobjects → Change something and then our app missing it. We could listen to metaobjects/update but this wouldn’t cover all cases.