Connectors: preload REST API responses on the Connectors screen#77353
Connectors: preload REST API responses on the Connectors screen#77353westonruter wants to merge 5 commits into
Conversation
The Connectors screen (options-connectors-wp-admin) previously fired
several REST API requests after hydration — site settings, a plugin
capability check, and one plugin record per registered connector —
delaying first paint.
Add a `{page-slug}-wp-admin_preload_paths` filter to the wp-build
page-wp-admin template so any generated admin page can register
page-specific preload paths. Hook it in the Connectors loader to
preload `/wp/v2/settings`, `OPTIONS /wp/v2/plugins`,
`/wp/v2/plugins/ai/ai?context=edit` (for the AI plugin callout),
and `/wp/v2/plugins/<basename>?context=edit` for each registered
connector with a plugin file.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Flaky tests detected in 2d6811f. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/27515021732
|
gziolo
left a comment
There was a problem hiding this comment.
I haven't tested, but these changes look solid to me.
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the 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. |
| $preload_paths[] = '/wp/v2/plugins/ai/ai?context=edit'; | ||
|
|
||
| // getEntityRecord( 'root', 'plugin', <basename> ) per connector in use-connector-plugin.ts. | ||
| if ( function_exists( 'wp_get_connectors' ) ) { |
There was a problem hiding this comment.
7.0 (and wp_get_connectors() ) is a pre-requesite for the AI plugin, and assumedly without we also don't care about prefetching wp/v2/settings or OPTIONS wp/v2/plugins.
Would it make sense to just early return at the top of the method instead of partially nesting this logic?
That said, if this is just about PHPStan (and the options-connectors...* filter won't even trigger otherwise ), then I'd just drop the always-true function_exists() and add a rule scoped // @phpstan-ignore <type> (<reason>)
There was a problem hiding this comment.
Good observation. I opted to remove the function_exists() check entirely in f3269d2 because this options-connectors-wp-admin_preload_paths filter only applies on the connectors admin page which only exists in 7.0 anyway.
Co-authored-by: justlevine <justlevine@git.wordpress.org>
jorgefilipecosta
left a comment
There was a problem hiding this comment.
The change looks good to me, I verified the preloaded endpoint payloads are passed to the client and on the smoke tests I did things look ok.
This PR needs the backport changelog entry and the corresponding core ticket/PR.
|
@jorgefilipecosta In testing this further, it seems the REST API preloading is not sufficient. In particular, it does not support preloading 404 responses. This means that it still does a REST API request for any plugin that has not been installed. If none of the plugins are installed, then there are always four 404 responses to the REST API:
I've addressed this in the core PR: WordPress/wordpress-develop#11790 |
|
|
||
| // AiPluginCallout in routes/connectors-home/ai-plugin-callout.tsx queries this | ||
| // hardcoded ID to check whether the WP AI plugin is installed/active. | ||
| $preload_paths[] = '/wp/v2/plugins/ai/ai?context=edit'; |
There was a problem hiding this comment.
With WordPress/wordpress-develop#11790 this could become as follows to serve the 404 responses to the preload middleware:
$preload_paths[] = array( '/wp/v2/plugins/ai/ai?context=edit', 'GET', array( 200, 404 ) );However, it would not be compatible with WP 6.9.
| // core-data's plugin entity uses the basename with `.php` stripped | ||
| // as the record key (see routes/connectors-home/use-connector-plugin.ts). | ||
| $basename = preg_replace( '/\.php$/', '', plugin_basename( $connector_data['plugin']['file'] ) ); | ||
| $preload_paths[] = '/wp/v2/plugins/' . $basename . '?context=edit'; |
There was a problem hiding this comment.
Ditto above, but it would not be compatible with WP 6.9:
$preload_paths[] = array( '/wp/v2/plugins/' . $basename . '?context=edit', 'GET', array( 200, 404 ) );
|
" This reverts commit 0065b16.
|
This was punted, so moving to next minor. |
t-hamano
left a comment
There was a problem hiding this comment.
Can we move the code added to lib/experimental/connectors/load.php to lib/compat/wordpress-7.0/connectors.php to move this PR forward? lib/experimental/connectors no longer exists.
…7.0/connectors.php The lib/experimental/connectors directory no longer exists on trunk; its contents moved to lib/compat/wordpress-7.0/. Relocate the _gutenberg_connectors_preload_paths() function and its filter from the deleted lib/experimental/connectors/load.php into lib/compat/wordpress-7.0/connectors.php to resolve the merge conflict. Co-authored-by: t-hamano <wildworks@git.wordpress.org> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@t-hamano I've resolved the conflicts:
Note the non-precached 404 responses are addressed in WordPress/wordpress-develop#11790, within which zero REST API requests appear when accessing the Connectors screen. |
| * Pages that know which requests their JS will issue on mount can add | ||
| * them here so the responses are embedded in the initial HTML rather | ||
| * than fetched over the network after hydration. | ||
| * |
There was a problem hiding this comment.
At first in keeping with #78826 it seems this is needed:
| * | |
| * | |
| * @since 7.0.1 | |
| * |
However, since this filter gets generated for every templated admin page, the docs would then incorrectly indicate 7.0.1 when a new admin page is added in a later release. So it seems this should be left out. This is a docs issue for these new admin routes.



What?
The Connectors screen (
options-general.php?page=options-connectors-wp-admin) fires several REST API requests after hydration, delaying the Largest Contentful Paint:Specifically:
GET /wp/v2/settings(site entity),OPTIONS /wp/v2/plugins(thecanUser( 'create', 'plugin' )check), a hardcodedGET /wp/v2/plugins/ai/aiused by the AI-plugin callout, and oneGET /wp/v2/plugins/<basename>per registered connector — five sequential round-trips with the default connectors.Why?
These responses are all trivially knowable at render time on the server, so they should be embedded in the initial HTML via
createPreloadingMiddlewarerather than re-fetched by the client after hydration. This also reduces the number of requests on the server, reducing server load.Measured impact (median LCP over 10 runs)
How?
Adds a
{page-slug}-wp-admin_preload_pathsfilter to thewp-buildpage-wp-admintemplate so any generated admin page can register page-specific preload paths. Hooks it in the Connectors loader to preload the five paths listed in What? above (the existing hardcoded preload in the template only covers the root site fields, not the settings entity the UI actually resolves, so/wp/v2/settingsis added too).After the change, none of the connector-related REST requests fire on page load — they are served from the preload middleware:
The two remaining requests come from
@wordpress/core-abilities, which Gutenberg enqueues on every admin page — out of scope here, but a candidate for similar preloading.Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Opus 4.6
Used for: Investigation, implementation, performance benchmarking, PR description composition