[wp-trac] [WordPress Trac] #64926: REST API: GET requests fail object/array schema validation when params are JSON-serialized strings
WordPress Trac
noreply at wordpress.org
Tue Apr 7 12:31:31 UTC 2026
#64926: REST API: GET requests fail object/array schema validation when params are
JSON-serialized strings
-------------------------+-------------------------------------------------
Reporter: dsmy | Owner: (none)
Type: defect | Status: new
(bug) |
Priority: normal | Milestone: Awaiting Review
Component: REST API | Version:
Severity: normal | Resolution:
Keywords: has-patch | Focuses: javascript, rest-api, php-
| compatibility
-------------------------+-------------------------------------------------
Comment (by westonruter):
Something that just came to mind: the abilities in the Abilities API can
be exposed in the REST API. When an ability is read-only, then a `GET`
request is made. Nevertheless, the `input_schema` may involve an object of
properties.
Consider this Abilities API code:
{{{#!php
<?php
add_action(
'wp_abilities_api_init',
static function () {
wp_register_ability(
'abilities-experiment/get-post',
array(
'label' => __( 'Get Post',
'abilities-experiment' ),
'description' => __( 'Gets fields
for a post', 'abilities-experiment' ),
'category' => 'site',
'input_schema' => array(
'type' =>
'object',
'required' => array(
'post' ),
'properties' => array(
'post' => array(
'type' =>
'object',
'required' =>
array( 'id' ),
'properties' =>
array(
'id' =>
array(
'type' => 'integer',
'description' => __( 'The ID of the post to get.', 'abilities-experiment'
),
'minimum' => 1,
),
)
),
'fields' => array(
'type' => 'array',
'items' => array(
'type' =>
'string',
),
)
),
),
'output_schema' => array(
'type' =>
'object',
),
'execute_callback' => static function (
array $input ): array|WP_Error {
$post = get_post(
$input['post']['id'] );
if ( ! $post ) {
return new WP_Error(
'post_not_found', __( 'Post not found.', 'abilities-experiment' ), array(
'status' => 404 ) );
}
$data = $post->to_array();
if ( $input['fields'] ) {
$data =
wp_array_slice_assoc( $data, $input['fields'] );
}
return $data;
},
'permission_callback' => static function (
array $input ): bool {
return current_user_can(
'edit_posts', $input['post']['id'] );
},
'meta' => array(
'annotations' => array(
'readonly' => true,
'destructive' => false,
'idempotent' => false,
),
'show_in_rest' => true,
),
)
);
}
);
}}}
Using the Abilities API client module as follows:
{{{#!js
(await import("@wordpress/abilities")).executeAbility(
"abilities-experiment/get-post",
{ post: { id: 1 }, fields: ["post_content", "post_author"] },
);
}}}
This results in an HTTP request being made to the REST API as follows:
`/wp-json/wp-abilities/v1/abilities/abilities-experiment/get-
post/run?input%5Bpost%5D%5Bid%5D=1&input%5Bfields%5D%5B0%5D=post_content&input%5Bfields%5D%5B1%5D=post_author&_locale=user`
With the brackets decoded:
`/wp-json/wp-abilities/v1/abilities/abilities-experiment/get-
post/run?input[post][id]=1&input[fields][0]=post_content&input[fields][1]=post_author&_locale=user`
So, note that `@wordpress/abilities` (er, `@wordpress/core-abilities`) is
already handling this correctly, converting a nested object into a list of
URL query params.
See
[https://github.com/WordPress/gutenberg/blob/6ea168835304764d253d169906148cf5fe6269d9/packages
/core-abilities/src/index.ts#L27-L71 logic] in `createServerCallback()`:
{{{
// For GET and DELETE requests, pass the input as query parameters.
path = addQueryArgs( path, { input } );
}}}
This `addQueryArgs()` function is part of the `@wordpress/url` package:
https://developer.wordpress.org/block-editor/reference-guides/packages
/packages-url/#addqueryargs
So you can use that instead of the `qs` package.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/64926#comment:25>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list