In a plugin I’m developing, I registered a custom REST API route to get some post data passing a post id:
function rest_api_init() {
register_rest_route( 'custombase/v1', '/post/(?P<id>[d]+)', array(
'methods' => 'GET',
'callback' => 'get_post_rest',
'permission_callback' => '__return_true',
'args' => array(
'id' => array(
'validate_callback' => function( $param, $request, $key ) {
return is_numeric( $param );
}
),
),
) );
}
function get_post_rest( WP_REST_Request $request ) {
$post = get_post( $request['id'] );
if ( is_wp_error( $post ) ) {
return $post;
}
$data = array(
'ID' => $post->ID,
'title' => $post->post_title,
);
$response = new WP_REST_Response( $data, 200 );
return $response;
}
I checked and the route is registered correctly as I can get info visiting, for example, the following URL:
https://example.com/wp-json/custombase/v1/post/1239
This is the result:
{"ID":1239,"title":"My example post title"}
As I understand, I need to use the apiFetch utility to make REST API requests to custom routes in Gutenberg. This is what I’m doing:
const mypost = apiFetch( { path: 'custombase/v1/post/1239' } ).then( ( post ) => {
return post;
} );
But, instead of post object, I’m getting a Promise with the post object inside when I do a console.log( mypost ):
Promise { <state>: "pending" }
<state>: "fulfilled"
<value>: Array [ {…} ]
0: Object { ID: 1239, title: "My example post title" }
length: 1
<prototype>: Array []
How can I get the post object instead?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
I’m getting a
Promisewith the post object inside when I do a
console.log( mypost )
Yes, because apiFetch() indeed returns a Promise object, and apiFetch() doesn’t assign the response received from the server to the mypost constant, and the return value of your then() callback is also not going to be assigned to the mypost variable.
How can I get the post object instead?
As suggested by React, you would want to add mypost as a local state in your component, then make your XHR/AJAX request (using apiFetch(), the native window.fetch(), Axios or whatever) in the componentDidMount() method of a class component, or use the useEffect hook in a function component.
Example using apiFetch() with useEffect:
import { useState, useEffect } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
function MyComponent( { post_id } ) {
const [ error, setError ] = useState( null );
const [ mypost, setPost ] = useState( null );
const [ isLoaded, setIsLoaded ] = useState( false );
useEffect( () => {
apiFetch( { path: `custombase/v1/post/${ post_id }` } ).then(
( result ) => {
setIsLoaded( true );
setPost( result );
},
( error ) => {
setIsLoaded( true );
setError( error );
}
);
}, [ post_id ] );
if ( error ) {
return <p>ERROR: { error.message }</p>;
} else if ( ! isLoaded ) {
return <p>Loading post { post_id }..</p>;
} else if ( mypost && mypost.id ) {
return <h3>Post <i>{ mypost.title || '#' + mypost.id }</i> loaded!</h3>;
}
return <p>No such post</p>;
}
// Sample usage: <MyComponent post_id="1239" />
Alternate Solution: Add an entity for your custom endpoint into the list of entities in the block editor.
-
Add the entity using
addEntities():import { dispatch } from '@wordpress/data'; dispatch( 'core' ).addEntities( [{ baseURL: '/custombase/v1/post', // The 'post' is not a post type - it's the "post" as in /post above. Also, "kind" // and "name" are not documented, so let's assume they form the above baseURL.. kind: 'custombase/v1', name: 'post', label: 'Post or whatever', }] ); // You can, for example, call the above (i.e. add the entity) before you register your // block type. -
Then use
getEntityRecord()to fetch post data from the endpoint:const mypost = select( 'core' ).getEntityRecord( 'custombase/v1', 'post', 1239 ); // Note that getEntityRecord() caches the results. -
And note that for
getEntityRecord()to work correctly, your endpoint callback must use the lowercaseidand notID:// In the get_post_rest() function: $data = array( 'id' => $post->ID, // use id and *not* ID 'title' => $post->post_title, );
So for example using useSelect, the above component (MyComponent) could now look like so:
import { useSelect } from '@wordpress/data';
function MyComponent( { post_id } ) {
const { mypost, isLoading } = useSelect( ( select ) => {
const args = [ 'custombase/v1', 'post', post_id ];
return {
mypost: select( 'core' ).getEntityRecord( ...args ),
isLoading: select( 'core/data' ).isResolving( 'core', 'getEntityRecord', args )
};
}, [ post_id ] );
if ( isLoading ) {
return <p>Loading post { post_id }..</p>;
} else if ( mypost && mypost.id ) {
return <h3>Post <i>{ mypost.title || '#' + mypost.id }</i> loaded!</h3>;
}
return <p>No such post</p>;
}
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0