Search for a keyword across post types in a Gutenberg component

The question:

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?

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

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;
}, [] );

Method 2

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 );
        } )
    }
};


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

Leave a Comment