Skip to Content

Shared fields

Every staging model inherits from a base that adds a small set of fields available on every resource. Document once, use everywhere.

Identity & tracing

How Graftport links a staged record back to its source row.

original_idstringrequired

The source platform’s stable ID for this row. Always emit it from your mapping — without it Graftport can’t reconcile re-extracts.

It’s automatically converted to a custom.original_id metafield on the destination record so future runs can match the row back.

"original_id": entity_id
source_url_pathstring

The URL path the row had on the source platform. Used by the redirect resource to set up old → new URL maps. Becomes a custom.source_url_path metafield.

"source_url_path": "/" & url_key & ".html"
idstring

The Shopify ID for this record. Almost never set in mapping — Graftport resolves IDs at load time using original_id. Set explicitly only if you’re upserting a record whose Shopify ID you already know.

excludebooleandefault false

Set to true to skip this row entirely — no staged record is produced, nothing reaches Shopify. Useful for filtering out test data, draft rows, or anything the merchant doesn’t want migrated.

"exclude": status = "draft" or sku = ""

Custom metafields

Pour any merchant-specific attribute into a metafield. Type is auto-detected unless you override.

dict_metafieldsobject

A flat dict of metafields. Keys become metafield keys; values become metafield values, with the type auto-detected from the value:

Value typeAuto-detected metafield type
stringsingle_line_text_field (or multi_line_text_field if it contains newlines)
intnumber_integer
floatnumber_decimal
boolboolean
list / dictjson

Empty values (null, "", [], {}) are silently skipped — no metafield is created.

"dict_metafields": { "warranty_years": warranty_years, "fragile": fragile = "yes", "specs": { "weight_kg": weight, "color": color } }

To force a specific Shopify type, use the {value, type} form:

"dict_metafields": { "rich_description": { "value": rich_html, "type": "rich_text_field" }, "expiry": { "value": expires_at, "type": "date" } }
metafield_prefixstringdefault custom

The namespace used for every entry in dict_metafields. Defaults to custom. Override per resource when you want a different namespace (e.g. magento to mark fields that came from Magento).

"metafield_prefix": "magento"

The two reserved metafields (original_id, source_url_path) ignore this — they always go in custom.

Shopify metafield types

When you use the {value, type} form in dict_metafields, the type must be one of Shopify’s supported metafield types. Common ones:

TypeValue formatUse for
single_line_text_fieldstringShort text.
multi_line_text_fieldstringLong text or any value with line breaks.
rich_text_fieldstring of Shopify rich-text JSONHTML / formatted descriptions.
number_integerintegerCounts, IDs.
number_decimalstring decimalPrices, ratings.
booleantrue / falseFlags.
dateYYYY-MM-DDCalendar dates.
date_timeISO8601Timestamps.
urlstring URLExternal links.
color#rrggbbHex colors.
weightJSON {value, unit}Weights with unit.
dimensionJSON {value, unit}Dimensions.
jsonany JSONFree-form structured data.
list.single_line_text_fieldJSON array of stringsTag-like lists.
list.product_referenceJSON array of product GIDsRelated products.

Shopify’s full list lives in their metafield types documentation .

Patterns

Conditional metafields

"dict_metafields": { "warranty_years": warranty_years > 0 ? warranty_years : null, "fragile": fragile, "lead_time_days": lead_time }

null/empty values are dropped automatically — no need to wrap each in a conditional unless the JSONata expression itself would error on missing fields.

Looping a source attribute array into metafields

"dict_metafields": $reduce( custom_attributes, function($acc, $attr) { $merge([$acc, { $attr.code: $attr.value }]) }, {} )

Mixing auto-detected and forced types

"dict_metafields": { "instructions": care_instructions, "color": { "value": color_hex, "type": "color" }, "ships_after": { "value": available_from, "type": "date" } }

Gotchas

original_id is doing real work. Don’t drop it from your mapping — Graftport uses it to keep extracts and loads idempotent. Without it, every re-run treats every row as new.

  • exclude: true doesn’t delete anything previously loaded. It just stops new staging records being produced. To remove an already-loaded record, use the migration’s purge tooling.
  • Metafield keys must be 2-64 chars and use snake_case. Keys with spaces or special chars are rejected by Shopify.
  • dict_metafields values are skipped if empty — a key with null, "", [], or {} produces no metafield at all. This is usually what you want; if you genuinely need to set an empty value, use the {value, type} form with a non-empty placeholder.
  • Auto-detection picks multi_line_text_field for any string with newlines. If you want plain single_line_text_field for a string that might contain newlines, force the type.
Last updated on