Repository navigation
Add block data to REST API - #2649
adamsilverstein wants to merge 80 commits into
Conversation
|
@BE-Webdesign thanks for pointing out #2503 - I searched for a similar PR and couldn't find one. As you pointed out, these PRs are pretty different; from a brief view it looks like #2503 is an API for (reusable) block objects themselves, while this PR is focused on exposing a (gutenberg built) post's blocks (and their data) |
rmccue
left a comment
There was a problem hiding this comment.
I think rather than creating separate endpoints, we should build this into the existing endpoints instead. In particular .content on the post type is intentionally an object to allow adding extra properties.
(I'll follow this up in a minute; GH's review comment UI isn't great for leaving long comments.)
|
In the post object, It also seems like (maybe) Gutenberg distinguishes between rendered and raw block content, based on this PR. (I could be wrong there.) Given that, I'd probably add a new sub-field under Something like this: {
"content": {
"raw": "<!-- gutenberg comments --> interspersed with HTML",
"rendered": "<div class='block-foo'><p>bar</p></div><p>interspersed with HTML</p>",
"blocks": [
{
"type": "foo",
"attributes": {
"value": "bar"
},
"rendered": "<p>bar</p>"
},
{
"type": "html",
"attributes": {
"content": "interspersed with HTML"
},
"rendered": "<p>interspersed with HTML</p>"
}
]
}
}(Disclaimer: I only know the data model in very broad strokes here.) |
While the two endpoints address different use cases, it'd be highly useful to have both endpoints represent a block in a consistent way, e.g. {
"type": "core/paragraph",
"attributes": {
"content": "How are you?",
"dropCap": true
},
"content": "<!-- wp:core/paragraph {\"dropCap\":true} -->\n<p class=\"has-drop-cap\">How are you?</p>\n<!-- /wp:core/paragraph -->",
"rendered": "<p class=\"has-drop-cap\">How are you?</p>"
}
+1 |
Good point @noisysocks, I was simply passing the data back as provided by Thanks for the feedback @rmccue - I like your suggestion of adding this data onto the regular post endpoint content object - my only concern would be the overhead introduced in block parsing the post content, even if it hasn't been edited in gutenberg, seems like we need a performat |
|
I remapped the response fields to match #2503 and also moved the data to the post object response as content->blocks. Here is what the response from the demo page provided by my install of gutenberg looks like: https://gist.github.com/adamsilverstein/5369160faca6097c5e22e0256e380962 |
|
@noisysocks & @rmccue can you please take another look after my recent changes where I have addressed your suggestions. |
Won't this point become moot at the point where Gutenberg is the default editor anyway? Also, we could choose to only expose all the block pieces for |
There was a problem hiding this comment.
This can be $block_name instead
There was a problem hiding this comment.
This should be inverted with a continue instead:
if ( null === $block_name ) {
continue;
}
There was a problem hiding this comment.
This should be $attributes not $block['attrs']. Also, properties should always be set if they're exposed in a given context to avoid index errors.
There was a problem hiding this comment.
$block['rawContent'] -> $raw_content
Also, should this only be exposed for $context === 'edit'?
There was a problem hiding this comment.
This is perhaps misnamed when it is returned from the parser - its not really the raw content and doesn't include the html markers for example. This really represents the rendered html and is returned as content for a block (matching naming from #2503). As far as I can see, rendered is only used for a couple of block types where their content is dynamic, for example a recent post list.
There was a problem hiding this comment.
Can you review the data thats output, it seems public? we could show blocks only in edit context, but that would make my idea of using them for a front end data source less useful.
There was a problem hiding this comment.
Maybe gutenberg_add_blocks_to_post_resource()? This won't be used apart from the filter, so should probably be a prefixed name rather than a convenient one.
There was a problem hiding this comment.
The blocks field should always be set, even if it's null.
Also, this should probably use ->set_data() instead of reaching into the object.
|
+1 for this being a part of the regular content endpoint(s) and glad to see that #1516 was mentioned. Architecturally, we should be thinking about more than just default post content here too since the blocks concept will likely be used in other scenarios (i.e. widgets and menus) in the future. |
…_api_from_post_content
|
I filed two tickets in WordPress core hoping we can move forward efforts for this POC:
|
|
Doing some cleanup in the repository. I was wondering if this PR still worth keeping open. Probably needs to be redone entirely if we ever get to it. What do you think? |
|
I still think this is a good addition to the API surface, would be nice to pick it up |
|
I still feel this would be a valuable addition although I'm not sure when I will have time to pick it up again. I do see some recent updates on the core tickets though. |
Initialize the per-block item array, drop an unused variable and fix spacing so PHPCS passes after the trunk merge.
Also drops the unused block registry variable, which was the intent.
🤖 PR meta 🤖🎉 PropsUnlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @BE-Webdesign, @loremipson, @khromov. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. Props Bot commandsYou can trigger Props Bot actions by commenting on this PR:
Updated as activity occurs, without notifying anyone named here. Add the |
Rename block keys to match WP_Block properties (name, attributes, inner_blocks, inner_html, inner_content) and apply them recursively, as requested in review. Register the filters on rest_api_init for every REST-enabled post type that supports the editor, so the opt-out filter can actually run. Hide blocks for password-protected posts the same way content.rendered is hidden, honor _fields, drop whitespace-only freeform blocks, and describe content.blocks in the item schema. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vwYjWL8VuAH7kt7zZdZi4
The blocks carry the raw stored markup, so expose them under the same rules as content.raw: the edit context and permission to edit the post. Make the field opt-in through _fields so existing clients, including the editor's own edit context loads, don't pay for parsing and a doubled payload. Drop the opt-out filter, skip posts that already have blocks, and describe the nested items in the schema. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vwYjWL8VuAH7kt7zZdZi4
Hooking registered_post_type picks up post types registered at any point, including late on rest_api_init, instead of only the ones that existed when rest_api_init first ran. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vwYjWL8VuAH7kt7zZdZi4
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vwYjWL8VuAH7kt7zZdZi4
|
Picking this one back up, six years later! Thanks @mtias for keeping it alive. Claude went back through the old review threads, here is what changed:
|
Apply Block Hooks before parsing so content.blocks and content.raw describe the same tree, and add blocks to revision and autosave responses, whose schemas already inherit the field from the parent. Drop the per-post edit_post check: in the edit context the posts controller already leaves out posts the user cannot edit, so the plugin now behaves exactly like the core patch. Let the schema callback accept a non-array schema from another filter and keep a blocks definition core already provides. Cover freeform content, _fields as an array, custom post types, post types without content, hooked blocks, revisions and autosaves, and reset the REST server after each test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vwYjV3Gi53g5ct8BDL5JE
This pull request adds Gutenberg block data to the post endpoints
This provides a way to get the data and content of each block of a Gutenberg-built page from the front end. This would be very useful for building component based front ends, because the components could map one-to-one with gutenberg blocks. With these endpoints, an App could easily get the data it needs to render each component. This might also provide a patch for a standard component library matching Gutenberg blocks.
Brought up to date with Claude Code in November 2026, its summary of where this landed:
Testing Instructions
/wp-json/wp/v2/posts/<id>?context=edit&_fields=id,content.blocks.content.blocksholds the block tree, nested blocks underinner_blocks._fields, or switch tocontext=view, andcontent.blocksis no longer returned./wp-json/wp/v2/posts?context=edit&status=draft,publish&_fields=id,content.blocks. Only the contributor's own posts come back, each with blocks./wp-json/wp/v2/posts/<id>/revisions/<revision id>?context=edit&_fields=id,content.blocksand the revision's blocks come back too.npm run test:php -- --filter Gutenberg_REST_Blocks_TestAI Use
The original code is mine from 2017-2020. Claude Code did this round: the trunk merge, the review fixes, the tests, the core backport, and a second pass for Block Hooks, revisions and test gaps, plus this description. I will review and test.
🤖 Generated with Claude Code
https://claude.ai/code/session_018vwYjWL8VuAH7kt7zZdZi4