Skip to content

fix(templates): nest the REST endpoint so 'schema' survives register_rest_route()'s upgrade step (C) - #16

Merged
akshat009 merged 1 commit into
mainfrom
fix/rest-schema-route-nesting
Aug 30, 2026
Merged

fix(templates): nest the REST endpoint so 'schema' survives register_rest_route()'s upgrade step (C)#16
akshat009 merged 1 commit into
mainfrom
fix/rest-schema-route-nesting

Conversation

@akshat009

Copy link
Copy Markdown
Owner

Closes the remaining half of item C from the open-issues report — the previous PR (#15) implemented get_item_schema() correctly but wired it into register_routes() at the wrong nesting level, so core silently dropped it.

The bug

register_rest_route() upgrades a flat args array to multiple endpoints whenever it sees a top-level 'callback' key — wrapping the entire array as a single numerically-indexed entry:

if ( isset( $args['callback'] ) ) {
    $args = array( $args );
}

'schema' was a sibling of 'callback' in that same flat array, so it got swallowed into that numeric entry along with everything else. get_data_for_route() looks for schema among the route's string keys, where it no longer existed — OPTIONS /wp-json/.../items kept returning no schema, the exact gap #15 was meant to close.

The fix

Nest the endpoint definition one level deeper and lift 'schema' out as a genuine route-level option, matching how core's own controllers do it (WP_REST_Settings_Controller et al):

register_rest_route(
    $this->namespace,
    '/' . $this->rest_base,
    array(
        array(
            'methods'             => \WP_REST_Server::READABLE,
            'callback'            => $this->get_items( ... ),
            'permission_callback' => $this->get_items_permissions_check( ... ),
            'args'                => array( /* … */ ),
        ),
        'schema' => $this->get_public_item_schema( ... ),
    )
);

get_item_schema() and the tests/bootstrap.php stub extension from #15 were both already correct and are untouched.

Regression test

The existing test_register_routes only asserts the third argument is an array (Mockery::type('array')), which passes regardless of nesting — it could not have caught this. Added a test that spies on the actual call:

public function test_register_routes_exposes_schema_as_a_route_option(): void {
    Functions\expect( 'register_rest_route' )
        ->once()
        ->andReturnUsing( function ( $route_namespace, $route, $args ) {
            $this->assertArrayHasKey( 'schema', $args, '...' );
            $this->assertArrayHasKey( 0, $args, '...' );
            return true;
        } );

    ( new Rest_Controller() )->register_routes();
}

Verification

  • npm test (CLI's own suite): 69/69.
  • Real end-to-end run against a generated --modules rest_api,caching scaffold:
    • composer test13/13 PHPUnit tests, 19 assertions (was 12/12 before this regression test).
    • composer lint15/15 files, 0 errors / 0 warnings. First run flagged the new test's closure parameter named $namespace as a reserved-keyword warning (WPCS NoReservedKeywordParameterNames) — caught by that same lint run and fixed by renaming to $route_namespace before landing.

D (test suite depth) remains out of scope, per the report.

🤖 Generated with Claude Code

…rest_route()'s upgrade step (C)

register_rest_route() upgrades a flat args array to multiple endpoints
whenever it sees a top-level 'callback' key -- wrapping the *entire*
array as a single numerically-indexed entry. Passing 'schema' as a
sibling of 'callback' put it inside that entry too, where core's
get_data_for_route() never looks for it: OPTIONS requests still
returned no schema despite get_item_schema() being fully implemented.

Fix: nest the endpoint definition one level deeper and lift 'schema'
out as a true route-level option, matching core's own controllers
(WP_REST_Settings_Controller et al).

Added a regression test that spies on the actual register_rest_route()
call and asserts 'schema' is a route option (not nested inside the
endpoint) and the endpoint itself is present at the numeric key --
the existing test only asserted the third arg is *an* array, which
passes regardless of nesting and could not have caught this.

Verified end-to-end against a real --modules rest_api,caching
scaffold: composer test -> 13/13 PHPUnit tests (19 assertions,
was 12/12 before the regression test), composer lint -> 15/15 files,
0 errors/0 warnings (one reserved-keyword warning from the new test's
$namespace closure param, caught by the same lint run, renamed to
$route_namespace before landing).
@akshat009
akshat009 merged commit 4dffd80 into main Aug 30, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant