Skip to content

🔬 Spike: mapping Gutenberg Blocks to the WPGraphQL Schema #3950

Description

@jasonbahl

Why this spike

This supersedes #1764 (a 2021 "server registry spec" conversation-starter). Since then the landscape has changed substantially: WordPress core now registers blocks server-side via register_block_type + block.json with a block-types REST endpoint for introspection, several ecosystem plugins have shipped different answers for getting block data into GraphQL, and WordPress 6.9/7.0's AI work has added new server-side primitives. There's renewed potential to leverage a server-side block registry to map blocks to GraphQL types.

This is a large, high-impact, and genuinely unsettled design space, so the goal here is not to commit to a solution but to define the problem(s), assess the current state of Gutenberg, and recommend a direction (likely a follow-on RFC, and possibly a new featured plugin rather than core).

First job: identify the problem(s)

Block support means different things to different people, and we should not design until we've named what we're solving. Two broad consumer camps to weigh:

  • Rendered delivery — fetch the rendered block markup (and/or the React/server-rendered output) and drop it in.
  • Structured delivery — fetch each block's structured attributes and innerBlocks and map them to the consumer's own components, which may be React, Swift, Svelte, or anything else. The premise: wp-admin authors with React blocks; headless apps query blocks and their data and render them however they like.

Both are valid; the spike should assess demand for each rather than assume.

Current landscape / prior art

Four notably different approaches already exist, worth studying for what works and what doesn't:

Core's stance to date: expose raw post content and leave block parsing to these plugins.

A candidate model (starting point, not a decision)

The wp-graphql-content-blocks overall shape is appealing as a baseline to pressure-test: each Block Type maps to a GraphQL Type, blocks share common interface(s), and each field that can be input/saved on a block is queryable on the block (with innerBlocks handled recursively). A server-side block registry could drive this mapping automatically.

Bigger possibilities to investigate (deliberately ambitious)

  • Registry-driven type mapping: auto-generate GraphQL types from the server-side block registry + block.json attribute schemas.
  • Block input mutations: now that we have formal oneOf input support, investigate block inputs for mutations.
  • Per-block-instance identity: if we can store/derive a stable unique ID per block instance, a node ID could look like post:123:block:<blockId>, enabling querying an individual block via node( id: ... ) and an updateBlock() mutation to mutate a single block rather than the whole post. (If we can solve individual block mutations, individual block subscriptions fall out of the same identity work, but that only becomes relevant if and when WPGraphQL has a formal subscription solution, so it's out of scope for this spike beyond noting it here.) Hard problem: blocks aren't stored as discrete records, they're serialized inside post_content, so stable identity and granular writes are non-trivial.
  • Delivery vehicle: a new featured plugin (in the model of WPGraphQL for ACF and WPGraphQL Smart Cache) vs core. Leaning toward a featured plugin given the surface area and the upstream churn risk.

WordPress 7.0 server-side tailwind (validate)

WordPress has long lacked good server-side block support, a gap I raised repeatedly over the years (see the historical through-line below). WP 6.9/7.0's AI investment moved things forward: PHP-only block and pattern registration, the Abilities API (server-side in 6.9, JS in 7.0) backing the AI Client and Connectors, and expanded Block Bindings / Pattern Overrides. AI consumers fundamentally need to understand what blocks exist and what inputs they accept, so this is the most aligned upstream momentum we've ever had.

But the WordPress 7.0 Field Guide does not document a formal, REST-introspectable block-type/attribute schema for external consumers, so the foundation may be necessary-but-not-sufficient. Validating exactly what the Abilities API and PHP block registration expose, and whether block.json attributes are introspectable server-side / via REST, is a primary task of this spike, and determines how much WPGraphQL must pioneer versus map.

Historical through-line

I've been making this case publicly since 2017:

  • Gutenberg#2751 (Sept 2017): I argued a comprehensive server-side block registry should be the source of truth for block capabilities, attributes, and their types, the way register_post_type and taxonomies are, so REST, WPGraphQL, alternative editors/page builders, headless apps, and alternate storage (ElasticSearch mappings, future wp_blocks tables) can all read one definition. The WPGraphQL vision I sketched there is today's candidate model: each block a typed GraphQL node (HeadingBlock), context-aware block queries (contentBlocks(where: { context: MOBILE })), __typename per block. The blocker I named then still holds: WPGraphQL can't expose a block's attributes or their enum options unless they're declared server-side.
  • graphql-js#207 (Dec 2017): I argued GraphQL needed input unions so blocks could be mutated, not just queried (query is easy with unions since each saved block has a concrete type; mutation needs the client to pass __typename + structured input). That input-union ask is essentially what shipped as oneOf, which is why this spike's block-mutation investigation is newly viable.

Open questions to answer in the spike

  1. Gutenberg / block state across interfaces: how complete is PHP-side block registration now (block.json attribute schemas, block bindings, synced/reusable patterns, interactivity API)? Critically, how can blocks be read and written outside the Gutenberg JS editor, via the WP REST API, WP-CLI, and other interfaces? Blocks are serialized in post_content and reusable blocks are a wp_block CPT, but is there first-class block read/write in REST beyond that? And what does the WP 7.0 Abilities API actually expose about blocks and their inputs, is any of it REST-introspectable today? Strong REST/CLI/abilities support signals a clear path for GraphQL integration; little beyond reusable blocks means there's more for WPGraphQL to pioneer, at higher cost and risk.
  2. Identity & storage: can we derive or persist a stable per-instance block ID without fighting post_content serialization? What breaks on re-save/reorder?
  3. Schema modeling: interface design, attribute typing, innerBlocks recursion, reusable/synced patterns, deprecated block variants.
  4. Mutations: oneOf block inputs, granular updateBlock vs whole-post writes, validation/sanitization, conflict handling.
  5. Rendered vs structured delivery: do we expose rendered markup, structured attributes, or both, and how?
  6. Core vs featured plugin: where does this live, and what (if anything) belongs in core to enable it?
  7. Custom Scalars / Types: what new scalars or object/interface types would WPGraphQL need to model blocks well (e.g. for block attributes, color/spacing/typography presets, media, rich text)?
  8. Relationship to 🎯 Tracking: mapping WordPress data registries (settings, meta, options) to the WPGraphQL Schema #3931 (mapping WordPress data registries to the Schema): is the block registry just another registry we map, and would we extend register_block_type with GraphQL args (a show_in_graphql-style opt-in + type mapping) the same way post types, taxonomies, settings, and meta are handled? This spike's registry-driven mapping is a sibling of 🎯 Tracking: mapping WordPress data registries (settings, meta, options) to the WPGraphQL Schema #3931's settings/meta/options mapping.

Deliverables

  • A problem-definition writeup (which problems we're solving, for whom).
  • A current-state assessment of Gutenberg's server-side block registry, the WP 7.0 Abilities API, and the four prior-art plugins.
  • A recommended direction, and if warranted, a follow-on RFC and/or a featured-plugin proposal.

Out of scope (for now)

Committing WPGraphQL core to block support. That decision is an output of this spike, not an input.

Metadata

Metadata

Assignees

No one assigned

    Labels

    component: architectureRelating to fundamental architectural decisionseffort: highMore than a weekimpact: highUnblocks new use cases, substantial improvement to existing feature, fixes a major bugneeds: discussionRequires a discussion to proceedscope: apiIssues related to access functions, actions, and filterstype: spike 🏐An issue that needs more research before becoming actionable

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    🆕 New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions