Get post from custom REST endpoint in Gutenberg

(Revised answer to properly address the questions in the question..)

For Question 1 (JS)

How can I get a post object from a custom REST endpoint within a
Gutenberg block?

So there’s the apiFetch() which you can use to fetch resources from the REST API.

But unlike return { children: getEntityRecords() }, return { children: apiFetch() } won’t work because apiFetch() always returns a Promise instance — getEntityRecords() always returns an object/array upon successful API request (and that the response is actually good).

So specifically if you want that to work with withSelect(), then you can add your endpoint as an entity:

  1. So in PHP, you can register a custom endpoint like so:

    register_rest_route( 'my-namespace/v1', 'posts', array( ...your args... ) )
    
  2. And in Gutenberg, you can add it as an entity like so:

    var dispatch = wp.data.dispatch;
    dispatch( 'core' ).addEntities( [
        {
            name: 'posts',           // route name
            kind: 'my-namespace/v1', // namespace
            baseURL: '/my-namespace/v1/posts' // API path without /wp-json
        }
    ]);
    

And then in your custom block:

// Used with withSelect():
return {
    children: select( 'core' ).getEntityRecords( 'my-namespace/v1', 'posts' )
}

For Question 2 (PHP)

I’d like to get a post in a form that looks identical to one retrieved
using getEntityRecords.

  1. You can create your very own controller class.

  2. Or maybe extend the WP_REST_Posts_Controller class (which is used with the standard posts routes like wp/v2/posts).

  3. Or just make use of the prepare_item_for_response() and prepare_response_for_collection() methods in WP_REST_Posts_Controller. These two methods can be used to return a list of posts.

Basic example for the third option (in actual implementation, you may want to add authorization/permissions check and/or accept custom parameters like per_page):

class My_REST_Posts_Controller {

    public function __construct() {
        $this->namespace="/my-namespace/v1";
        $this->resource_name="posts";
    }

    public function register_routes() {
        register_rest_route( $this->namespace, "https://wordpress.stackexchange.com/" . $this->resource_name, array(
            array(
                'methods'  => 'GET',
                'callback' => array( $this, 'get_items' ),
            ),
        ) );
    }

    public function get_items( $request ) {
        $post_type="post"; // or this can be defined as a property in the class
        $posts = get_posts( array(
            'post_type' => $post_type,
        ) );

        $data = array();

        if ( empty( $posts ) ) {
            return rest_ensure_response( $data );
        }

        // Here we make use of the default controller class.
        // Remember that only a single post type is supported.
        $class = new WP_REST_Posts_Controller( $post_type );

        foreach ( $posts as $post ) {
            $response = $class->prepare_item_for_response( $post, $request );
            $data[] = $class->prepare_response_for_collection( $response );
        }

        return rest_ensure_response( $data );
    }
}

Leave a Comment