Skip to Content

Blog & article mapping

StagingBlog represents one article plus the blog it belongs to. There’s no separate “blog” resource — every staging row migrates one article, and Graftport creates the blog container automatically the first time it sees a new blogName + blogSlug pair.

Quick reference — minimum loadable article

{ "original_id": post_id, "blogName": "News", "blogSlug": "news", "title": title, "body": content_html, "authorName": author, "publishDate": published_at }

Blog reference

Pick exactly one of these two strategies — by name (creates if missing) or by ID (assumes the blog exists).

blogNamestring

The blog’s display name. Graftport creates the blog container on first sight of a new blogName + blogSlug pair, then reuses it for subsequent articles.

"blogName": "News"
blogSlugstring

URL slug for the blog (the bit after /blogs/). Used together with blogName to identify or create the blog.

"blogSlug": "news"
blogIdstring

Shopify blog GID, if you already know the destination blog’s ID. Setting blogId skips the create-blog logic — Graftport posts the article directly into the existing blog.

Use this when the merchant already has a Shopify blog set up and you’re migrating articles into it.

Article content

titlestringrequired

Article headline.

"title": title
handlestring

URL slug for the article. Auto-derived from title if omitted.

"handle": url_key
bodystring

The article body, as HTML. Convert from Markdown / plain text in the mapping if your source uses something else.

"body": content_html
summarystring

Excerpt shown in blog index pages.

"summary": short_description
authorNamestring

Author display name. Just a string — there’s no user record to link to. Capture rich author data (email, bio) in dict_metafields if the merchant needs it.

"authorName": author_name ? author_name : "Editor"
publishDatestring (ISO8601)

When the article was first published on the source platform. Drives the displayed date — always emit it from the source for accurate timeline preservation.

"publishDate": published_at
isPublishedbooleandefault true

Whether the article is publicly visible. false creates the article in draft state — exists in admin but not on the storefront.

"isPublished": status = "published"
tagsstring[]

Article tags. Used by the storefront for filtering.

"tags": tag_list

Featured image

One image per article. Different shape from product images — uses image_src + image_alt, not files.

image_srcstring

URL of the featured image.

"image_src": featured_image_url
image_altstring

Alt text. Defaults to the article title if omitted (set explicitly for better accessibility).

Patterns

Magento CMS post → Shopify article

{ "original_id": post_id, "blogName": "Stories", "blogSlug": "stories", "title": title, "handle": identifier, "body": content, "summary": short_content, "authorName": author_name ? author_name : "Editor", "publishDate": publish_time, "isPublished": is_active = "1", "image_src": featured_image, "image_alt": title, "tags": tags }

Many posts under one blog (most common)

If every post in your source belongs to one blog, hard-code the blog fields:

{ "original_id": post_id, "blogName": "News", "blogSlug": "news", "title": title, "body": body_html, "authorName": author, "publishDate": created_at }

The first article creates the news blog; every subsequent article attaches to it.

Multiple source sections → multiple blogs

{ "original_id": post_id, "blogName": section.name, "blogSlug": section.slug, "title": title, }

Each unique (blogName, blogSlug) pair becomes its own Shopify blog.

Posting into an existing Shopify blog

{ "original_id": post_id, "blogId": "gid://shopify/Blog/123456789", "title": title, "body": body, "authorName": author, "publishDate": published_at }

Set blogId and skip blogName / blogSlug. Useful when the merchant has already set up a custom blog with a specific theme template.

Capturing rich author data as metafields

{ "original_id": post_id, "blogName": "News", "blogSlug": "news", "title": title, "body": body, "authorName": author.name, "publishDate": published_at, "dict_metafields": { "author_email": author.email, "author_bio": author.bio, "author_avatar": author.avatar_url } }

Gotchas

One staging row = one article. If you have N articles, your mapping should produce N output rows. Don’t try to bundle multiple articles into a single staging row.

  • body must be HTML. Markdown won’t render — convert in the mapping (or pre-convert in the source extract) before emitting.
  • Inline images stay as <img> tags inside the body. Only the featured image goes in image_src. Inline image URLs need to resolve from the destination shop — the merchant may need to rehost old image URLs after migration.
  • publishDate controls the displayed date, not the load time. Always emit it for historical accuracy.
  • authorName is just a string. No user account is created.
  • Theme migration is out of scope. This resource moves blog content, not the theme that displays it. The destination Shopify store needs a theme with blog templates already configured.
  • Blog dedup: Graftport identifies a blog by its (blogName, blogSlug) pair on first sight. Changing either between articles in the same migration creates a second blog. Be consistent.
  • Article handles must be unique within a blog, not across blogs. summer-sale in /blogs/news/ and /blogs/seasonal/ are fine.
Last updated on