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​

  • Product → getProductUrl() (Magento core's canonical resolver)
  • Category → getUrl()
  • 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.openGraph — Object.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​