Shopify app metafields and metaobjects 

Metafields 

Shopify metafields provide a means to expand Shopify's data model by storing additional information on various resources such as orders, products, customers, and the shop itself. They are commonly utilized in custom and public app development to enhance the functionality and user experience of Shopify apps.

While metafields are valuable for app development, working with them can be challenging for developers. The data is stored in a separate resource known as the "metafield object" on Shopify's end, whereas developers typically prefer the data to be directly associated with the resource they are extending. Moreover, Shopify's API rate limits can add complexity to the process of navigating across these additional resources.

To simplify the developer experience, Gadget offers a streamlined solution that enables fast synchronization, querying, and mutation of Shopify metafields within the backend of the app. This feature provides developers with efficient tools to manage metafields, reducing the complexities associated with working with metafields and improving the overall development workflow.

Storing metafield data in Gadget 

Gadget allows you to store metafield data directly on any of the following Shopify resources:

  • App Installation
  • Collection
  • Company
  • Company Location
  • Customer
  • Draft Order
  • Location
  • Market
  • Order
  • Product
  • Product Image
  • Product Variant
  • Shop

To do this, you must first copy the desired resource to your Gadget backend. Go to the Connections screen and pick the desired scopes and models (e.g. the read_products scope and the Product model), and connect to a store.

Once you have the desired model in your Gadget backend, click on Add Field to decorate the model with the additional metafield values. Name your field and select an appropriate type for the incoming data. To start the metafield import, you also need to click the Store data from Shopify Metafield box.

The required fields to set up a metafield on a Shopify Model

You now need to register your metafield's Namespace. Enter the Namespace that is used for your metafield in the Shopify Store, then click Register Namespace. Gadget will then register for webhooks on the entered Namespace.

The namespace for a metafield is registered

Once you get a message stating that the Namespace registrations are complete, you can enter the key for your metafield and select the correct metafield type.

Here's an example of what the form looks like when we try to teach Gadget to store a metafield capturing each product variant's weight under the Namespace "gadget_app" and key "spiciness":

A completed Shopify metafield setup in Gadget

Your Gadget app is now set up to store the data directly on this model. You can go back to your Connections page and sync to pull in metafield data. Once the metafield data is synced to your Gadget app, you can query or mutate metafield values using Gadget Actions.

Storing reference metafield types as relationships 

You can store reference metafields in as relationship fields in Gadget. The reference id stored in the metafield will be automatically parsed by Gadget and, if the related record exists in your Gadget database, the relationship to the existing record will be created.

For example, if you want to store a page_reference metafield on your shopifyProduct model, you can:

  • set up the field on the shopifyProduct model using the above instructions
  • set the field type in Gadget to a belongs to relationship
  • set the inverse of the relationship as either a has one or has many so that, for example, shopifyPage has many shopifyProduct.
A screenshot of a page reference metafield stored on the shopifyProduct model, modeled as a relationship to the shopifyPage model in Gadget

Gadget will parse any incoming gid values (sent via the reference metafield types) from Shopify and create the relationship. You can then access related record information as you would with any other relationship in Gadget.

Creating a relationship to a custom model 

You can store a reference to a custom model record in a metafield. The metafield value in Shopify can be stored as a number_integer or single_line_text_field metafield type. The value stored in the metafield should be the related custom model record's id in your Gadget database.

For example, if you have a custom model called bike and you want to store a reference to a bike record on a shopifyProduct model as a metafield, you can:

  • set up the field on the shopifyProduct model using the above instructions
  • set the field type in Gadget to a belongs to relationship
  • set the inverse of the relationship as either a has one or has many so that, for example, bike has one shopifyProduct
A screenshot of an integer metafield stored on the shopifyShop model, modeled as a relationship to a custom model in Gadget

You can then store id values for a bike record as a metafield on products in Shopify. Gadget will automatically create the relationship on the incoming webhook, and you can access bike record information from your shopifyProduct records like you would any other relationship in Gadget.

Querying metafields 

Gadget's approach to metafields offers two key advantages:

  1. Fetching data: You can retrieve metafield values alongside the records they are associated with in a single query. This simplifies the data retrieval process and reduces the number of API requests needed.

  2. Range queries: Gadget allows you to perform range queries directly on the metafield values. This enables you to apply filters, comparisons, or other conditions on the metafield values, providing more flexibility in data retrieval operations.

Gadget's metafield implementation facilitates efficient querying of metafield data alongside corresponding records and supports range queries for more advanced filtering and comparison operations.

Metafield query examples 

Here's an example of a range query being run on “spiciness”, a metafield stored on the Shopify Product, in order to fetch products that have a spiciness rating of greater than 5:

GraphQL
1query {
2 shopifyProducts(
3 filter: { spiciness: { greaterThan: 5 } }
4 sort: { spiciness: Ascending }
5 ) {
6 edges {
7 node {
8 title
9 id
10 spiciness
11 handle
12 }
13 }
14 }
15}

Writing to metafields 

To mutate and sync metafield values in Gadget, you need to define the mutation logic in code. This is done by writing code within your action functions that Gadget executes when the API is triggered.

Metafield writing examples 

Here's an example of a Run function added to the update action of a Shopify Product model that sends the metafield information to Shopify:

JavaScript
1/**
2 * Action code for update on Shopify Product
3 * @param { import("gadget-server").UpdateShopifyProductActionContext } context - Everything for running this action, like the api client, current record, params, etc.
4 */
5export default async function run({ api, record, params, connections }) {
6 if (record.changed("title")) {
7 await connections.shopify.current?.graphql(
8 `mutation updateAMetafield($metafields: [MetafieldsSetInput!]!) {
9 metafieldsSet(metafields: $metafields) {
10 metafields {
11 id
12 value
13 }
14 }
15 }`,
16 {
17 metafields: [
18 {
19 key: "a_metafield",
20 namespace: "test321",
21 ownerId: `gid://shopify/Product/${record.id}`,
22 type: "single_line_text_field",
23 value: record.title + " as a metafield!",
24 },
25 ],
26 }
27 );
28 }
29}

This will only update the metafield value in Shopify! If you have registered the metafield on a Gadget model, the webhook that fires after the metafield is updated in Shopify will update the metafield value in Gadget.

We have an example of a more dynamic metafield update available as a gist on Github.

Migrating private metafields to app-owned metafields 

As Shopify will be deprecating the use of private metafields soon, we recommend Gadget apps currently relying on them make the necessary changes to migrate to app-owned metafields.

To sync or register webhooks for app-owned metafield namespaces you may use either prefix app--{your-app-id}--{namespace} or the shorthand $app:{namespace} reserved namespaces when setting up metafields.

For more information on the deprecation of private metafields check out Shopify's guide here.

Adding app-owned metafields 

Let's walk through an example of migrating your private metafield in Gadget to an app-owned metafield.

We recommend using the shorthand prefix ($app:{namespace}) as this works best when you deploy to production and will not require you to find and input the app ID.

If you do choose to use the longer namespace prefix: the app ID to input within the prefix can be found within the URL for your Shopify partners app.

  1. Head to the Shopify Partners dashboard
  2. Click on apps and select the app you wish to use with the metafield from the list
  3. Once selected - grab the URL from the browser.
  4. Within the URL (example: partners.shopify.com/2862902/apps/69412093953/overview) your app ID will be the second number, so in this case 6941209353.
  1. Head to the set metafield on your Shopify model
private metafield option
  1. Unselect the private metafield option on your Shopify model's metafield
  2. Within the namespace input you will now add the prefix before your original namespace - so now instead of the namespace being colors it will be $app:colors
  3. Now click on Register Namespace and once successfully registered you're all done
Input and register the new app-owned metafield

Metaobjects 

Metaobjects are similar to metafields, but they don't need to be tied to a Shopify resource. A metaobject definition can take any shape - metaobject fields are made up of groups of metafield types and can include references to Shopify resources, such as Orders or Products, or even references to other metaobjects!

Metaobjects can be created in the Shopify admin or using the metaobject API. Created metaobjects can be accessed directly in Liquid, using the Storefront API, or in the Admin API.

Metaobjects in Gadget 

To read or write metaobjects you need to add the Metaobject and Metaobject Definitions scopes when setting up or editing your Shopify connection.

It's important to note that:

  • The metaobjects API is available in API version 2023-01 and above
  • There are no models to import for metaobjects
  • Webhooks for metaobjects do not exist!

If you want to sync metaobject data from Shopify to Gadget, you will need to do so manually:

  • Create custom models in Gadget that match your metaobject definitions used to store the metaobject data
  • An action that can be triggered manually, on a schedule, or as part of some other action (ie. store install) that manually fetches metaobjects stored in Shopify. Note that you will need to deal with Shopify rate limits and add retry logic yourself.

Metaobject definitions 

You can use Shopify's GraphQL API to read, create, update, and delete metaobject definitions.

You can use your Gadget app's current Shopify connection to call these APIs to add or make changes to metaobject definitions on a merchant's store. For example, if you wanted to store some information about bikes (their style, model name, year, and a reference to the product record they pertain to), you would simply run the following code snippet:

shopifyShop/install/onCreate.js
JavaScript
1const metaobjectCreateMutation = `
2 mutation metaobjectDefinitionCreate($definition: MetaobjectDefinitionCreateInput!) {
3 metaobjectDefinitionCreate(definition: $definition) {
4 metaobjectDefinition {
5 name
6 type
7 }
8 userErrors {
9 field
10 message
11 }
12 }
13 }
14 `;
15
16// define a metaobject that can be used to store information for bicycles
17const createMetaobjectDefinition = await connections.shopify.current.graphql(
18 metaobjectCreateMutation,
19 {
20 definition: {
21 access: {
22 storefront: "PUBLIC_READ",
23 },
24 capabilities: {
25 publishable: {
26 enabled: true,
27 },
28 },
29 description: "Model information for bikes",
30 displayNameKey: "model",
31 fieldDefinitions: [
32 {
33 description: "Style of bike",
34 key: "style",
35 name: "Style",
36 type: "single_line_text_field",
37 required: true,
38 },
39 {
40 description: "Bike model name",
41 key: "model",
42 name: "Model",
43 type: "single_line_text_field",
44 required: true,
45 },
46 {
47 description: "Model year",
48 key: "year",
49 name: "Year",
50 type: "number_integer",
51 validations: {
52 name: "min",
53 value: "1950",
54 },
55 required: true,
56 },
57 {
58 description: "Bike product reference",
59 key: "bike_ref",
60 name: "Product reference",
61 type: "product_reference",
62 required: true,
63 },
64 ],
65 name: "Bike",
66 type: "bike",
67 },
68 }
69);

Querying metaobjects 

You can utilize the metaobject API in Gadget to query actual instances of metaobjects. By using the Shopify connection established in Gadget, you can create instances of a specific metaobject, such as a bike metaobject. It's important to note that you need to have a corresponding metaobject definition in place before creating instances of the metaobject.

shopifyShop/install/onCreate.js
JavaScript
1const metaobjectSeedDataMutation = `
2 mutation metaobjectCreate($metaobject: MetaobjectCreateInput!) {
3 metaobjectCreate(metaobject: $metaobject) {
4 metaobject {
5 handle
6 }
7 userErrors {
8 field
9 message
10 }
11 }
12 }`;
13
14// add a new instance of my "bike" metaobject
15const createMetaobjectSeedData = await connections.shopify.current.graphql(
16 metaobjectSeedDataMutation,
17 {
18 metaobject: {
19 type: "bike",
20 handle: "kona-dew-plus-2015",
21 capabilities: {
22 publishable: {
23 status: "ACTIVE",
24 },
25 },
26 fields: [
27 {
28 key: "style",
29 value: "hybrid",
30 },
31 {
32 key: "model",
33 value: "Dew Plus",
34 },
35 {
36 key: "year",
37 value: "2015",
38 },
39 {
40 key: "bike_ref",
41 value: "gid://shopify/Product/7965217390872",
42 },
43 ],
44 },
45 }
46);

You can also use the metaobjects query to read metaobject data. The following example uses the metaobject type field to fetch instances of a "bike" metaobject:

JavaScript
1await connections.shopify.current.graphql(
2 `query GetMetaobjects($type: String!) {
3 metaobjects(type: $type) {
4 fields {
5 key
6 reference
7 type
8 value
9 }
10 id
11 type
12 displayName
13 }
14}`,
15 { type: "bike" }
16);

References to metaobjects 

Even though metaobjects are stored independently of Shopify resources, Shopify also allows you to make associations between a metaobject and existing resources, such as products or orders, through metaobject references.

Metaobject references are stored on records as metafields and contain a reference to the metaobject. To capture this relationship in Gadget, you need to add a new metafield to a Shopify model in Gadget and select the "Metaobject reference" type. This field will then contain a reference to the metaobject in Shopify.