Populating data from custom endpoint

Hi,

I have many custom taxonomies and I’m trying to build custom search where user can search for different post types and filter results by taxonomies.
On the left side of the page I have a tree of all taxonomies for different post types, so user can click and go to the taxonomy page, where on the left side he will have the same menu with selected taxonomy.
What’s important - there will be custom routing, because I need to have URLs that can contain different taxonomies in one segment, for example:
https://exapmle.com/products-for-mens/tax1-category1.tax-1-category2/tax2.category1/?s=somequery

You can find similar logic on Zalando website:
https://www.zalando.co.uk/mens-shoes-loafers/aldo.barker/

To this stage everything is easy and great, but I have a problem with populating the results that I get from my custom endpoint.

For example, if user enters search page I will download in the handler for this url list of all categories and some amount of posts, to show it on this page. I have registered endpoint that returns array of categories from get_categories().

cat_ID: 113
cat_name: "Accusantium animi"
category_count: 4
category_description: "Some category description"
category_parent: 0
count: 4
description: "Some category description"
filter: "raw"
name: "Accusantium animi"
parent: 0
slug: "accusantium-animi"
taxonomy: "category"
term_group: 0
term_id: 113
term_taxonomy_id: 113

I have a handler that looks like this:

...
  name: 'isMainSearchResults',
  priority: 20,
  pattern: '(/search-results/)',
...
...

const response = await api.get({
  endpoint: `/${endpoint}/search-main`,
  params: {
    s: decodeQuery(query.s),
    type: pageType,
  },
});
    
const results = await response.json();

results returns object with list of all taxonomies and couple posts that I want to display. I get it from the backend with help of get_posts() function:

{
  blogCategories: [
    {... category object as above }
  ],
  blogPosts: [
    {
      ID: 82
      comment_count: "0"
      comment_status: "open"
      filter: "raw"
      guid: "https://example.com/admin/blog/distinctio-iusto-molestiae-aut-hic-nemo/"
      menu_order: 0
      ping_status: "open"
      pinged: ""
      post_author: "1"
      post_content: "<p>some content</p>"
      post_content_filtered: ""
      post_date: "2020-12-12 23:54:18"
      post_date_gmt: "2020-12-12 23:54:18"
      post_excerpt: ""
      post_mime_type: ""
      post_modified: "2020-12-12 23:54:18"
      post_modified_gmt: "2020-12-12 23:54:18"
      post_name: "distinctio-iusto-molestiae-aut-hic-nemo"
      post_parent: 0
      post_password: ""
      post_status: "publish"
      post_title: "Distinctio iusto molestiae aut hic nemo"
      post_type: "post"
      to_ping: ""
    }
  ]
  products: [
    ...same as posts
  ]
  ... other post types
}

Now I would like to map this data to the format that Frontity uses (or the one that WordPress REST API gives you?), and populate it, so user wouldn’t have to download this data every time the route / filter changes.

Is it possible to achieve something like this with Frontity?

I know I could preload all categories in beforeSSR and then only load posts on other pages, but then user would have to download it for every single page even if he doesn’t use it, which is not the best case scenario.

And the other problem of not populating this data is, that if I want to show list of posts on the custom page, using component that uses inside source.category the categories will not show until I go into the post page (cause the categories are not available in state.source.category and are not automatically fetched on my search page). Same with featured images etc.

I’m trying to achieve it with Frontity for last couple days and can’t find a way to do it :frowning: I will be really grateful for some explanation to these two problems.

Frontity works automatically with a set of links that can be fetched by using the actions.source.fetch() action. Some key ideas about this fetch action:

  • This fetch will perform the request AND populate the data to the state
  • This fetch is automatically done whenever you visit a related wordpress link
  • You can use as many times as you want with the same link, because fetch will take care of only doing the requests once

Besides this, you can create your own links with libraries.source.handlers (as you have already done). These custom links created with handlers, can also be fetched by using the actions.source.fetch() action

You can perform any logic you want in these custom handlers, but most of the times for every custom link that needs to be fetched you’ll want them to do at leasr two things:

  • 1. Getting the data → This can be done with the libraries.source.api.get() method that will help you to perform any custom request you need.
  • 2. Populating the data to the Frontity state → This can be done with the libraries.source.populate() method that can populate the response got with libraries.source.api.get() into the state

These are the official methods so they are 100% secure to be used in Frontity projects (Dynamic SSR React Apps where requests could be done from eerver side or slient side)

But, you could use your own ways of getting the data and merging this data into the state

Here you have an explanation regarding doing requests to WpGraphQL and merging these responses into the state

Hope this helps

Thank you for such a detailed answer!

I managed to solve the above problem, but I still have a troubles with the populate() function.

WhenI fetch a custom post type in my custom handler, everything works great. I get the following response:

[{
    type: 'food_product',
    id: 102707,
    link: '/food_product/some-product/'
}]

But, when I use the same pattern for custom taxonomies, the type property is undefined:

// API call:
const ingredientsResponse = await api.get({
  endpoint: `${pageType}_ingredient`, // (food_ingredient)
  params: {
    per_page: 40,
    _embed: true,
    search: searchQuery,
  },
});

// populate data to the state
const ingredients = await populate({
  response: ingredientsResponse,
  state,
  force,
});


// console.log(ingredients);
[{
    type: undefined,
    id: 1877,
    link: '/food_ingredient/test-ingredient/'
}]

What’s interesting - taxonomies are added to the state properly. But the response is missing type, so in my template I can’t access them in this way:

{ingredients.map(({ id, type, link }) => {
  const item = state.source[type][id]; // type here is undefined
    return (
      <Link link={link} key={id}>
        {item.name}
      </Link>
    );
})}

If I hardcode the type: (const item = state.source['food_ingredient'][id];) everything works fine.

Is this a bug or intended behaviour? :slight_smile: