Skip to main content

seo: SeoMetadata

The headless storefront's single source of truth for every SEO artifact a page needs.

Schema

type SeoMetadata @doc(description: "SEO metadata bundle for a single entity, designed for headless storefronts.") {
title: String @doc(description: "Rendered meta title — entity override or template.")
description: String @doc(description: "Rendered meta description.")
robots: String @doc(description: "Resolved robots directive (entity override > store default).")
canonical: String @doc(description: "Canonical absolute URL for the entity.")
open_graph: [SeoTag!]! @doc(description: "Open Graph and Twitter Card meta tags.")
structured_data: [String!]! @doc(description: "JSON-LD documents as raw JSON strings, ready to wrap in <script type=\"application/ld+json\">.")
}

type SeoTag @doc(description: "A single meta tag key/value.") {
name: String! # e.g. "og:title", "twitter:card"
content: String!
}

Available on:

  • ProductInterface — every product type (simple, configurable, virtual, downloadable, bundle, grouped)
  • CategoryInterface
  • CmsPage
  • CmsPageItem

Field-by-field

title

The same string Magento's storefront would render in the <title> tag:

  1. If the entity has a non-empty native meta_title → that value verbatim
  2. Else if meta templates are enabled and a template exists for the entity type → the rendered template (with global suffix applied)
  3. Else null

The character-limit truncation that seosuite:meta:generate applies is NOT applied here — the GraphQL field returns the canonical stored/rendered value. If you want stricter truncation client-side, do it in your storefront layer.

description

Same priority chain as title but for meta_description. Returns null if neither a native value nor a template renders to non-empty.

robots

Resolved precedence:

  1. Entity-level meta_robots (CMS page edit form / category edit form / product attribute) if non-empty AND not the literal string system
  2. Store config design/search_engine_robots/default_robots
  3. null

The string system is treated as a sentinel — admins picking "System Default" from the meta_robots dropdown produce that value, which means "fall back to the store default."

canonical

  • ProductgetProductUrl() (Magento core's canonical resolver)
  • CategorygetUrl()
  • CMS page<store_base_url>/<page_identifier>

Custom canonical mappings via byte8_seosuite_url are NOT applied at this layer — only on the storefront. If your headless storefront needs them, query the URL Relationship table directly via REST or extend the resolver.

open_graph

Flat list of {name, content} pairs. Names follow the standard og:* and twitter:* conventions. Empty array if Open Graph is disabled (byte8_seosuite/open_graph/is_active = 0).

The shape is deliberately flat (rather than a nested OpenGraph object) to map cleanly onto Next.js's Metadata.openGraphObject.fromEntries(open_graph.map(t => [t.name, t.content])) gives you a dict.

structured_data

List of JSON-LD documents as raw JSON strings. Already escaped (</<\/) so safe to drop into <script type="application/ld+json">{...}</script> via dangerouslySetInnerHTML in React.

For products: includes the Product schema + Breadcrumb (when both auditors enabled). For categories: Breadcrumb only. For CMS home page: Organization + WebSite. Otherwise empty array.

Resolver classes

Each entity type has its own resolver:

EntityResolver
ProductInterfaceByte8\SeoSuite\Model\Resolver\Product\Seo
CategoryInterfaceByte8\SeoSuite\Model\Resolver\Category\Seo
CmsPage / CmsPageItemByte8\SeoSuite\Model\Resolver\Cms\Seo

All three call the same stateless services:

  • Byte8\SeoSuite\Model\GraphQl\SeoMetadataBuilder — title, description, robots, canonical
  • Byte8\SeoSuite\Model\GraphQl\OpenGraphBuilder — OG/Twitter tags
  • Byte8\SeoSuite\Model\StructuredData\Builder\* — JSON-LD blocks

So the GraphQL output and the storefront output are guaranteed identical for the same entity in the same store context.

Caching

The response participates in Magento's GraphQL cache via the standard cat_p_<id>, cat_c_<id>, cms_p_<id>, cfg cache tags. Edits to the entity, store config, or SEO Suite config invalidate cleanly.

Heavy production sites should put Varnish in front and let Magento's tag-based purging handle invalidation.

Backward compatibility

The pre-v2.2 fields are preserved:

  • CategoryInterface.meta_robots: String — still resolved by Byte8\SeoSuite\Model\Resolver\Category\MetaRobots
  • CmsPage.meta_robots: String — still resolved by Byte8\SeoSuite\Model\Resolver\Cms\CmsPageMetaRobots
  • CmsPageItem.meta_robots: String — same resolver as CmsPage

Use the new seo field for everything new; the v1 fields are kept for backward compatibility but are subsumed by seo.robots.

Next