How-To: Fetch Pages with Identical Slugs in WordPress via REST API

Timm SedlaczekFull-Stack Developer
Published on18.11.2024
TagsWordpress, Headless, REST API

When a new page is created in the WordPress CMS, a URL slug is automatically generated based on the page title. To avoid conflicts, WordPress prevents duplicates from occurring both when automatically assigning and manually adjusting the slug. This is done, for example, by adding a suffix such as “-2”.

The situation is different if two pages with identical slugs are on different levels. A path such as /lorem/ipsum/lorem is therefore possible without any problems.

However, such multiple slugs can lead to problems if pages are retrieved using the slug via the REST API. This is particularly relevant if WordPress is used as a headless CMS and content is to be retrieved via the URL path.

In the above example, the REST API would return an array with two page objects when retrieving pages with the slug “lorem”. Without additional logic or further queries, it would not be possible for the frontend to clearly assign the correct page object.

Example of the status quo

Example for querying the page data with the parameter slug:

const response = await fetch(`${url}/wp/v2/pages?slug=lorem`);

In this case, the REST API would return an array with two page objects:

[
   {
      "id":29,
      "slug":"lorem",
      "status":"publish",
      "type":"page",
      "title":{
         "rendered":"Lorem"
      },
      "parent":26
   },
   {
      "id":22,
      "slug":"lorem",
      "status":"publish",
      "type":"page",
      "title":{
         "rendered":"Lorem"
      },
      "parent":0
   }
]

New REST API route for querying pages using the full URL path

One possible solution for this is to register an additional REST API route that returns the correct page based on a complete URL path.

Registration of the new REST API route:

function ts_register_page_by_path_route() {
    $namespace = 'wordpress/v2';
    register_rest_route( $namespace, '/pages/path', array(
        'methods'             => 'GET',
        'callback'            => 'ts_get_page_by_path',
        'permission_callback' => '__return_true',
        'args'                => array(
            'path' => array(
                'required' => true,
                'type'     => 'string',
                'validate_callback' => function( $param ) {
                    return is_string( $param ) && !empty( trim( $param ) );
                },
            ),
        ),
    ));
}
add_action( 'rest_api_init', 'ts_register_page_by_path_route' );

Callback function of the new REST API route in which the complete URL path is used by the function get_page_by_path to search for the appropriate page:

function ts_get_page_by_path( $request ) {
        $pathRequest = $request->get_param( 'path' );
        if (!$pathRequest) {
            return new WP_Error( 'no_path_provided', 'No path provided', array( 'status' => 400 ) );
        }
        $page = get_page_by_path($pathRequest);
        if (!$page) {
            return new WP_Error( 'page_not_found', 'No page with the given path found', array( 'status' => 404 ) );
        }
        $page->superkraft = $this->contentStore->sk_get_post_content($page->ID);
        return new WP_REST_Response($page, 200);
}

This article is for informational purposes only and does not constitute legal advice. It makes no claim to completeness or accuracy and reflects the personal opinion of the author. The article was originally written in German and has been translated into English with the help of a translation tool.