I am trying to get all posts from category products
. My template looks like this: /products/PARENT_CATEGORY/CHILD_CATEGORY
. What I need is all the posts from every CHILD_CATEGORY.
I try to access it via /posts?categories=28
, but it returns no posts, as the category with ID of 28 doesn’t have any posts itself. But its child, for example, category with ID of 40, has some posts. Should I access them like /posts?categories=40,41,42...
? Or there is another way to do it?
I had the same use case than you — calling WP-API from React — and ran into the same issue.
Unfortunately WP-API does not support filtering by parent categories. In order to support it, you could extend the API’s functionality, adding a new filter that will run a tax_query
behind the scenes, similar to Faye’s answer.
Assuming that you have access to the WordPress application, modify the functions.php
in the current theme adding:
// Add parent_category filter to REST API
function rest_filter_by_parent_category($args, $request)
{
if (isset($request['parent_category'])) {
$parent_category = sanitize_text_field($request['parent_category']);
$args['tax_query'] = [
[
'taxonomy' => 'category',
'field' => 'term_id',
'include_children' => true,
'operator' => 'IN',
'terms' => $parent_category,
]
];
}
return $args;
}
add_filter('rest_post_query', 'rest_filter_by_parent_category', 10, 3);
Then, just query the API with the new filter: /posts?parent_category=28
Alternative
If you cannot modify the functions.php
file (e.g. you are querying an external blog), then you could:
- Fetch all the categories (which you might have already done in your app).
- Build a reverse index, with the shape
[parentId: number] => number[]
- Build the query using
categories=C1,C2...
using the reverse index.
const axios = require("axios");
// Query all the categories from WordPress
const fetchCategories = async () => {
const params = [
"_fields[]=id",
"_fields[]=parent",
// Ideally you would use a better pagination strategy
"page=1",
"per_page=100",
];
// Using axios, but you could use native fetch or any other library
return axios
.get(`WORDPRESS_SITE/wp-json/wp/v2/categories?${params.join("&")}`)
.then((result) => result.data);
};
// Build reverse index
const buildParentIndex = (categories) => {
return categories.reduce((acc, category) => {
const hasParent = !!category.parent; // Root categories have ID 0
const parentId = hasParent ? category.parent : category.id;
if (!acc[parentId]) {
acc[parentId] = [];
}
if (hasParent) {
acc[parentId].push(category.id);
}
return acc;
}, {});
};
(async () => {
// You should pre-compute & cache these, as fetching the categories
// and building the index on every request will heavily affect
// the latency of your request
const categories = await fetchCategories();
const parentIndex = buildParentIndex(categories);
const fetchPostsByParentId = (categoryId) =>
axios
.get(
`WORDPRESS_SITE/wp-json/wp/v2/posts?categories${parentIndex[
categoryId
].join("&")}`
)
.then((result) => result.data);
})();
If possible, I’d suggest to go with the first approach — modifying functions.php
— as it’s simpler and more consistent. The JS alternative will likely require caching for that the latency not to be impacted, which brings a lot of potential issues (e.g. stale cache).
Source
- https://barebones.dev/rest-api-filter-posts-by-tax_query/
- How to display child posts in the parent category