Skip to main content

How To Create and Use Shopify Metafields in Next.js Commerce

Shopify Metafields help you customize different content types, like Products, by allowing you to save additional specialized information that isn't offered in the Shopify admin. You can access Metafields in the Shopify Admin by going to Settings->Custom Data, and then just follow the directions to create Metafields for the content data types that you want.

The Catch: Storefront API can't read default metafields

Unfortunately, the Metafields you create in the Shopify Admin are not accessible to the Storefront API, so they are useless for all headless implementatinos, including Next.js. Shopify mentions this restriction in their API Docs: By default, the Storefront API can't read metafields. To make specific metafields visible in the Storefront API.

The workaround suggested by Shopify is to use their metafieldStorefrontVisibilityCreate mutation to allow access to the metafields to the Storefront API. However, after knocking my head against the wall for an hour trying to get this mutation to work, I just gave up. I kept getting access errors with the mutation and nothing in the documentation or the forums about the topic seemed to solve the issue. I am not clear what this mutation actually does.

Luckily, I stumbled upon the solution.

The Solution: Create the Metafields via the Admin API

Shopify has a Graphql Admin API endpoint called metafieldDefinitionCreate which allows you to create custom metafields that can be used by the Storefront API.

To use this Admin API, of course, you need an Admin API Key, which you can get by creating a private app on Shopify and getting your API Key. You can then use that key an execute a Graphql mutation to create the required metafield. Below is a Node.js code example, you can use to execute this query.

const url = 'https://<YOURSHOPIFYSTOREID>.myshopify.com/admin/api/2024-01/graphql.json';

const QUERY = `mutation CreateMetafieldDefinition($definition: MetafieldDefinitionInput!) {
metafieldDefinitionCreate(definition: $definition) {
createdDefinition {
id
namespace
key
access {
admin
storefront
}
}
userErrors {
field
message
code
}
}
}
`;
//These variables are critical. Be careful here.
const VARIABLES= {
"definition": {
"name": "<NAMEOFMETAFIELD>", //This should be unique for each metafield
"namespace": "$app:custom-<NAME>", //your custom name space. You should probably use the same namespace for all the metafields used in your storefront
"key": "<uniquekey>", //unique key for space
//see types here https://shopify.dev/docs/apps/custom-data/metafields/types
"type": "list.page_reference",
//see owner types here https://shopify.dev/docs/api/admin-graphql/2024-01/enums/MetafieldOwnerType
"ownerType": "PRODUCT", //
"access": {
"admin": "MERCHANT_READ_WRITE",
"storefront": "PUBLIC_READ" //this is what gives the storefront api access!
}
}
}

const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': process.env.SHOPIFY_ADMIN_KEY //your Shopify Key
},
body: JSON.stringify({
query: QUERY,
variables: VARIABLES
})
};

const fetchAPI = async () => {
try {
const result = await fetch(
url,
options
);
const data = await result.json();
console.info(data);
return data
} catch (err) {
console.info(err)
}
}
const createMetaFields = await fetchAPI();
return createMetaFields

After you run this mutation, you will get a response back which will include the ID of the metafield, the namespace, and the key. It will help to save this information before doing anything else.

If you then log into your Shopify Dashboard, you will see the new Metafield in the custom data section per above. You can then begin using it in the dashboard and in the Storefront API, as we will get to next.

By the way, if you make a mistake creating the metafield, you can easily delete it, by using the code below.

const url = 'https://<YOURSHOPIFYSTOREID>.myshopify.com/admin/api/2024-01/graphql.json';
const QUERY = `mutation metafieldDefinitionDelete($id: ID!) {
metafieldDefinitionDelete(id: $id, deleteAllAssociatedMetafields: true) {
deletedDefinitionId
userErrors {
field
message
}
}
}
`;

const VARIABLES= {
//"deleteAllAssociatedMetafields": true,
"id": "gid://shopify/MetafieldDefinition/<SOMENUMBER>" //this is the id you get back when you created the metafild
}


const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': process.env.SHOPIFY_ADMIN_KEY
},
body: JSON.stringify({
query: QUERY,
variables: VARIABLES
})
};

const fetchAPI = async () => {
try {
const result = await fetch(
url,
options
);
const data = await result.json();
console.info(data);
//return data.data.metafieldStorefrontVisibilityCreate.userErrors
return data
} catch (err) {
console.info(err)
}
}
const deleteMeta = await fetchAPI();
return deleteMeta

Final Step: Using the Metafields in your Next.js Store

Add Metafields to your Product Graphql Query

In order to use the new metafields in your Next.js Commerce store, or any headless implementation for that matter, you will want to be sure to include the metafields you want in the Graphql Query you use to get the product information. In the Next.js Commerce store there is a product fragment query which you can use for this. See below for the addition of sample metafields. Just adjust for your own metafields. Please see the documentation at Shopify for more details on how we structured this query, Retrieve metafields

import imageFragment from './image';
import seoFragment from './seo';

const productFragment = /* GraphQL */ `
fragment product on Product {
id
handle
availableForSale
title
description
descriptionHtml
options {
id
name
values
}
priceRange {
maxVariantPrice {
amount
currencyCode
}
minVariantPrice {
amount
currencyCode
}
}
variants(first: 250) {
edges {
node {
id
title
availableForSale
selectedOptions {
name
value
}
price {
amount
currencyCode
}
}
}
}
featuredImage {
...image
}
images(first: 20) {
edges {
node {
...image
}
}
}
seo {
...seo
}
tags
updatedAt
keyDetails: metafield(namespace: "<REPLACEWITHYOURNAMESPACE>",key: "<REPLACEWITHYOUKEY>") {
value
type
}
questionsAnswers: metafield(namespace: "<REPLACEWITHYOURNAMESPACE>",key: "<REPLACEWITHYOUKEY>") {
value
type
}
}
${imageFragment}
${seoFragment}
`;

export default productFragment;

Use the Metafields in your Product Page

The final step is to simply get the information returned bythe product graphql query and show it on your product page. This information will returned in the products/[handle] /page.tsx page when you run this code

const product = await getProduct(params.handle); 

Just console.log product and you will see your metafields.

In order for the above to work, you must also be sure the export async function getProduct in /lib/shopify/index.ts and the reshapeProduct function include your metafields. They should be in the ...rest parameter. If they are not, console.log in that function and check the reshapeProduct function.

You can then access your metafields in the code, with something like:

const metafieldValue = product?.${METAFIELDKEYUSEDINGRAPHQLQUERY}?.value;
//example from our query: const questionsAnswers = product?.questionsAnswers?.value;

Of course, depending on the type of metafield you use, the code can get a bit complex to display the correct information. For example, if you use a reference type, Shopify will return a bunch of ids, and you will then have to loop thru these ids and query the API again to get the information about the reference resource. This type of mapping is beyond the scope of this post.