Search code examples
javascriptnode.jsstrapiheadless-cms

Modify default find service in Strapi


I have 2 collection types in my Strapi setup: product and review where a product has many reviews.

I want to add 2 new fields to the response of /products and /products/:id:

averageRaing: number
totalReviews: number

I want to override the default find service to implement this, but I am unable to find the source code for strapi.query("product").find(params, populate) to override it.

If possible, I need this done in a single query rather than making multiple queries.

So far I have:

find(params, populate) {
  return strapi.query("product").model.query(db => {
    // I want the same query as when I run `strapi.query("product").find(params, populate)`
  });
},

But I am unsure of how to handle the params and populate in the exact same way that .find(params, populate) does.


Solution

  • After digging into the source code, I found a solution:

    const { convertRestQueryParams, buildQuery } = require("strapi-utils");
    
    function find(params, populate) {
      const model = strapi.query("product").model;
      const filters = convertRestQueryParams(params);
      const query = buildQuery({ model, filters });
    
      return model
        .query((qb) => {
          const totalReviewsQuery = strapi.connections
            .default("reviews")
            .count("*")
            .where("product", strapi.connections.default.ref("products.id"))
            .as("total_reviews");
    
          const averageRatingQuery = strapi.connections
            .default("reviews")
            .avg("rating")
            .where("product", strapi.connections.default.ref("products.id"))
            .as("average_rating");
    
          query(qb);
    
          qb.column("*", totalReviewsQuery, averageRatingQuery);
        })
        .fetchAll({
          withRelated: populate,
          publicationState: filters.publicationState,
        })
        .then((results) => results.toJSON());
    }