关于 shopify-metafields
此技能指南说明如何使用 Shopify metafields 和 Metaobjects 为商品、订单、客户等资源添加结构化自定义数据,适合构建定制 storefront 和内容模块的开发者。内容涵盖定义、API 读写、Liquid 展示及数据类型验证。
主要功能
- 01为 Shopify 资源添加结构化数据
- 02通过定义验证字段类型
- 03使用 Admin API 写入 metafields
- 04在 Liquid 和 Storefront API 中读取数据
- 05创建可复用的 Metaobjects
适用场景
- 01为商品补充尺寸、护理说明或认证信息
- 02保存客户等级或订单配送说明
- 03在主题中管理 FAQ、尺码指南等内容
- 04创建可重复使用的团队或规格内容条目
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill shopify-metafields安装后在 Claude Code 中输入/shopify-metafields确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
tile.jsonevals/metafield-definitions-and-bulk-write/criteria.jsontask.mdevals/metaobjects-for-structured-content/criteria.jsontask.mdevals/storefront-api-metafield-queries/criteria.jsontask.md详细介绍
Shopify 元字段
概述
元字段让你无需单独构建数据库,即可为 Shopify 资源添加结构化自定义数据,例如产品、变体、订单、客户、系列和页面。元字段定义会强制执行类型验证(文本、数字、日期、URL、JSON、文件、产品引用等),并让元字段可用于 Liquid 模板编辑器和 Storefront API。元对象扩展了这一概念,可创建可重复使用的独立自定义数据结构。
何时使用此技能
- 当产品需要 Shopify 默认字段以外的其他属性时(护理说明、尺寸、认证)
- 当需要存储每位客户的数据时,例如忠诚度等级、订阅状态或 B2B 账号
- 当需要在主题中使用元字段引用构建由内容管理的版块时(常见问题、尺码指南、功能亮点)
- 当需要添加结账时提供的订单级自定义数据时(礼品留言、配送说明)
- 当需要使用元对象创建可重复使用的结构化内容条目时(团队成员、媒体报道、规格)
核心说明
-
通过 Admin API 创建元字段定义
定义会强制执行类型,并使元字段可供店面访问:
// Create a metafield definition for product care instructions const response = await adminClient.request(` mutation CreateMetafieldDefinition($definition: MetafieldDefinitionInput!) { metafieldDefinitionCreate(definition: $definition) { createdDefinition { id name namespace key type { name } } userErrors { field message code } } } `, { variables: { definition: { name: "Care Instructions", namespace: "custom", key: "care_instructions", type: "multi_line_text_field", ownerType: "PRODUCT", description: "Washing and care instructions for the product", visibleToStorefrontApi: true, // Optional: pin to product admin UI (use pinnedPosition in API version 2023-10+) pinnedPosition: 1, }, }, });可用类型:
single_line_text_field、multi_line_text_field、number_integer、number_decimal、date、date_time、boolean、url、json、color、weight、volume、dimension、rating、file_reference、product_reference、variant_reference、collection_reference、page_reference、metaobject_reference、list.<type>。 -
通过 Admin API 写入元字段
// Set a metafield on a product export async function setProductMetafield( productId: string, namespace: string, key: string, value: string, type: string ) { const response = await adminClient.request(` mutation SetMetafield($metafields: [MetafieldsSetInput!]!) { metafieldsSet(metafields: $metafields) { metafields { id key namespace value } userErrors { field message code } } } `, { variables: { metafields: [ { ownerId: productId, namespace, key, value, type, }, ], }, }); return response.data.metafieldsSet; } // Example: Set care instructions on a product await setProductMetafield( "gid://shopify/Product/1234567890", "custom", "care_instructions", "Machine wash cold. Tumble dry low. Do not bleach.", "multi_line_text_field" ); -
在 Liquid 模板中读取元字段
定义设置了
visibleToStorefrontApi: true后,即可通过metafields对象在 Liquid 中使用元字段:{% comment %} product.metafields.namespace.key {% endcomment %} {% if product.metafields.custom.care_instructions != blank %} <div class="care-instructions"> <h3>Care Instructions</h3> {{ product.metafields.custom.care_instructions | metafield_tag }} </div> {% endif %} {% comment %} Access a product reference metafield {% endcomment %} {% assign related = product.metafields.custom.related_product.value %} {% if related %} <a href="{{ related.url }}">{{ related.title }}</a> {% endif %} {% comment %} Access a list of file references (images) {% endcomment %} {% for image in product.metafields.custom.gallery_images.value %} <img src="{{ image | image_url: width: 800 }}" alt="{{ image.alt }}"> {% endfor %} -
通过 Storefront API 读取元字段
// Query product with metafields in the Storefront API const { data } = await storefront.request(` query GetProductWithMetafields($handle: String!) { product(handle: $handle) { id title # Metafields must be explicitly requested by namespace + key careInstructions: metafield(namespace: "custom", key: "care_instructions") { value type } relatedProduct: metafield(namespace: "custom", key: "related_product") { reference { ... on Product { id title handle featuredImage { url altText } } } } certifications: metafield(namespace: "custom", key: "certifications") { references(first: 5) { edges { node { ... on Metaobject { id fields { key value } } } } } } } } `, { variables: { handle: "my-product" } }); -
创建和使用元对象
元对象是独立的自定义数据条目,适用于常见问题、客户评价或任何结构化内容:
// Create a Metaobject definition await adminClient.request(` mutation { metaobjectDefinitionCreate(definition: { name: "FAQ Entry" type: "faq_entry" fieldDefinitions: [ { name: "Question", key: "question", type: "single_line_text_field", required: true } { name: "Answer", key: "answer", type: "multi_line_text_field", required: true } { name: "Sort Order", key: "sort_order", type: "number_integer" } ] }) { metaobjectDefinition { id type } userErrors { field message } } } `); // Create a Metaobject entry await adminClient.request(` mutation { metaobjectCreate(metaobject: { type: "faq_entry" fields: [ { key: "question", value: "What is your return policy?" } { key: "answer", value: "We accept returns within 30 days of purchase." } { key: "sort_order", value: "1" } ] }) { metaobject { id handle } userErrors { field message } } } `);
示例
批量导入产品属性元字段
// Import dimensions for multiple products at once (up to 25 per request)
export async function bulkSetDimensions(
products: Array<{ id: string; weight: number; length: number; width: number; height: number }>
) {
const metafields = products.flatMap(({ id, weight, length, width, height }) => [
{ ownerId: id, namespace: "custom", key: "weight_grams", value: weight.toString(), type: "number_integer" },
{ ownerId: id, namespace: "custom", key: "length_cm", value: length.toString(), type: "number_decimal" },
{ ownerId: id, namespace: "custom", key: "width_cm", value: width.toString(), type: "number_decimal" },
{ ownerId: id, namespace: "custom", key: "height_cm", value: height.toString(), type: "number_decimal" },
]);
// Process in batches of 25 (API limit)
for (let i = 0; i < metafields.length; i += 25) {
const batch = metafields.slice(i, i + 25);
await adminClient.request(`
mutation SetMetafields($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
userErrors { field message }
}
}
`, { variables: { metafields: batch } });
}
}
在 Liquid 中渲染常见问题元对象
{% comment %} sections/faq.liquid {% endcomment %}
{% assign faqs = shop.metafields.custom.faq_entries.value %}
<div class="faq-section">
<h2>Frequently Asked Questions</h2>
{% for faq in faqs %}
<details class="faq-item">
<summary>{{ faq.question.value }}</summary>
<div class="faq-answer">{{ faq.answer.value | newline_to_br }}</div>
</details>
{% endfor %}
</div>
最佳实践
- 写入元字段前务必先创建定义——定义可启用类型验证、店面 API 访问和 Admin UI 字段编辑器;未定义的命名空间/键组合会显示为原始 JSON
- 商家管理的数据使用
custom命名空间——其他命名空间(例如app_name)留给应用自有数据,避免商家直接编辑 - 对需要在主题或无头前端中读取的定义设置
visibleToStorefrontApi: true——元字段默认是私有的 - 使用
metafieldsSet批量写入元字段——每次变更最多接受 25 个元字段;批量操作时使用它,而不是单独调用productUpdate - 对富文本和文件引用元字段使用 Liquid 的
metafield_tag过滤器——它会根据元字段类型渲染正确的 HTML 元素(img、p 等) - 结构化的多字段数据优先使用元对象,而非 JSON 元字段——元对象具有强类型,并且可在 Shopify Admin UI 中编辑内容
- 记录命名空间规范——确定一套约定(商家使用
custom.*,应用使用yourapp.*),并记录所用的键,方便开发者查找
常见问题
| 问题 | 解决方案 |
|---|---|
| Metafield 在 Storefront API 中返回 null | Metafield 定义必须将 visibleToStorefrontApi: true 设为 true;如果创建时未设置此标志,请更新定义 |
Liquid 中的 metafields.custom.key 为空 | 确保存在对应 namespace/key 的定义;Liquid 仅公开已注册定义的 metafield |
| 列表类型 metafield 的值是 JSON 字符串,而非数组 | 在 Liquid 中使用 ` |
metafieldsSet 因 TYPE_MISMATCH 失败 | value 必须是与类型匹配的 JSON 序列化字符串;对于 number_integer,请传入 "42",而非 42 |
| Metaobject 字段未更新 | 使用 metaobjectUpdate mutation,并提供 metaobject GID 和 fields 数组;支持部分更新 |
| 应用的 namespace 与其他应用冲突 | 使用应用的 handle 作为 namespace 前缀(例如 myapp-handle),以避免在共享商店中发生冲突 |
相关技能
- @shopify-admin-api
- @shopify-storefront-api
- @shopify-app-development
- @shopify-theme-development
- @custom-product-attributes