0

In a Gutenberg component I'm developing, I have a function that searches for a keyword across multiple post types specified in a constant:

const postTypes = [ 'posts', 'pages', 'portfolio' ];

const onSearchStringChange = ( keyword ) => {
    Promise.all( postTypes.map( postType => apiFetch( {
        path: `/wp/v2/${ postType }?search=${ keyword }`,
    } ) ) ).then( ( results ) => {
        console.log( results.reduce( ( result, final ) => [...final, ...result], [] ) );
    } )
};

The function works as expected and returns an array of results.

Instead of using a static array of post types, I'd like to get the viewable post types (minus the attachment post type) of a site via a function. This is what I'm using:

const postTypes = useSelect( ( select ) => {
    const { getPostTypes } = select( 'core' );
    const excludedPostTypes = [ 'attachment' ];
    const filteredPostTypes = getPostTypes( { per_page: -1 } )?.filter(
        ( { viewable, slug } ) => viewable && ! excludedPostTypes.includes( slug )
    );
    const result = ( filteredPostTypes || [] ).map( ( { slug } ) => slug );
    return result;
}, [] );

The function also works as expected and I get an array of slugs of all viewable post types minus attachment.

But, if I pass the resulting array to the onSearchStringChange function I'm getting the following error:

Uncaught (in promise) 
Object { code: "rest_no_route", message: "No route was found matching the URL and request method.", data: {…} }
​code: "rest_no_route"
​data: Object { status: 404 }
​message: "No route was found matching the URL and request method."
​<prototype>: Object { … }

I suppose that the post types array is not fully resolved at the time of calling the onSearchStringChange function. Even if I do a postTypes.length > 0 check before the apiFetch I get the same error.

So, my question is: how can I make sure that the post types array is fully resolved when calling onSearchStringChange?

0
3

I'm answering myself for the benefit of others. I was passing an array of post type slugs to apiFetch instead of passing an array with their rest_base parameter. So, the correct function should be:

const postTypes = useSelect( ( select ) => {
    const { getPostTypes } = select( 'core' );
    const excludedPostTypes = [ 'attachment' ];
    const filteredPostTypes = getPostTypes( { per_page: -1 } )?.filter(
        ( { viewable, slug } ) => viewable && ! excludedPostTypes.includes( slug )
    );
    const result = ( filteredPostTypes || [] ).map( ( { rest_base } ) => rest_base );
    return result;
}, [] );
1
  • 1
    This is indeed a helpful answer 👍+1
    – Sally CJ
    Jun 2 at 10:09
1

Instead of doing multiple apiFetch calls for each post type, a more optimal solution to make a search across multiple post types is to use the WP REST API search endpoint, which I didn't know it existed until now. The downside is that you don't get the full post objects using this endpoint, but if you only need the post id, title, url and type, it's ok:

const postTypes = useSelect( ( select ) => {
    const { getPostTypes } = select( 'core' );
    const excludedPostTypes = [ 'attachment' ];
    const filteredPostTypes = getPostTypes( { per_page: -1 } )?.filter(
        ( { viewable, slug } ) => viewable && ! excludedPostTypes.includes( slug )
    );
    const result = ( filteredPostTypes || [] ).map( ( { slug } ) => slug );
    return result;
}, [] );

const onSearchStringChange = ( keyword ) => {
    if ( postTypes.length > 0 ) {
        apiFetch( {
            path: `/wp/v2/search?search=${ keyword }&type=post&subtype=${ postTypes.join() }`,
        } ).then( ( results ) => {
            console.log( results );
        } )
    }
};

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.