Discussion regarding default population of REST

I’ve opened a GitHub issue, where you can add your voice/context.

1 Like

Already fixed this one in v4.0.2 :slight_smile:

Well for populating you own data without having a lot of query in every call you could just modify the find content-type controller like this:

// PATH: ./src/api/[content-type]/controllers/[content-type].js

"use strict";

 
/**
 *  event controller
 */
 
const { createCoreController } = require("@strapi/strapi").factories;
 
module.exports = createCoreController("api::event.event", ({ strapi }) => ({
    async find(ctx) {
      ctx.query = { ...ctx.query, populate: '*' }
    
      // Calling the default core action
      const { data, meta } = await super.find(ctx);
  
      // some more custom logic
  
      return { data, meta };
    }
}));

Strapi: 4.0.4

2 Likes

I’m using the @nuxtjs/strapi plugin for Nuxtjs v2 and this method is not working for me. I am thinking why should I use Dynamic Zones and Components if I cannot query them properly and I have to use work-arounds. The concept of Dynamic Zone is great of itself but they way it is used it makes it look crippled.

1 Like

Not sure if this is the best way but I’ve managed to set some default population logic for specific collection types using middleware.

For example. If i wanted every request for books to return the author relation without having to worry about providing it in the query string:
Books Router config: /api/book/routes/book.js

module.exports = {
  routes: [
    {
      method: "GET",
      path: "/books",
      handler: "book.find",
      middlewares: ["populate-books"]
    },
    // ... more routes (with the same middleware if you want)
  ]
}

And then in /api/book/middlewares/populate-books.js:

const populateList = ["author"];

const enrichCtx = (ctx) => {
  if (!ctx.query) {
    ctx.query = {};
  }
  const currentPopulateList = ctx.query.populate || [];
  ctx.query.populate = [...currentPopulateList, ...populateList];
  return ctx;
};

module.exports = (config, { strapi }) => {
  return async (context, next) => {
    const newCtx = enrichCtx(context);
    context = newCtx;
    await next();
  };
};

This means that any find request for books will return the author, whether the populate query param has been passed or not. If it has been passed in, it will retain the query that’s been asked for as well as enriching it with authors.

I suppose if you wanted to “reverse” the default populate logic you could change the enrichCtx function only use the query from the request if it’s present, and if not, use the default one you defined?

Not sure if middlewares or policies is the best place for this kind of logic. (Or even if it’s not a good idea in general) :thinking:

Hope that helps someone though!

2 Likes

Excelente amigo :+1::+1::+1:
Estuve atascado por esto 15 dias.


Translated by @DMehaffy

Excellent friend :+1::+1::+1:
I was stuck on this for 15 days.

Hey there,

I know this is already an old topic, but I have to express, that I am deeply unhappy with the way this is handled right now.

populate=* should populate everything the way Strapi V3 did it. That means up to 3 levels including dynamic zones, media, relations and nested components.

The new API-Features are great but we definitely need a fast track to get all the data we need (including the 3rd level).
Being able to be selective and giving the REST API almost GraphQL like abilities is nice, but complicating the way to get all data is not a good choice.

4 Likes

I have to agree with @pix3l, this is an incredibly annoying default. The security and performance reasons pointed to by @roelbeerens are true but you shouldn’t have to rewrite a controller to get such basically functionality. We built out website from one of your templates using dynamic zones and as pointed out by @Georg this breaks those without either putting in a redundant populate=* in every page query or rewriting the controller. Surely having some property on a relation field that allows users to choose from within the content type whether that relation populates by default would be much more user friendly and intuitive, though I can’t speak for the difficulty of implementing it. I really hope you alter this and at the very least make it clear in the migration guide that this is a change from v3

6 Likes

Has there been any answer by the Strapi Team?

There are countless questions regarding this on the discord. And this will never stop, since its so uncommon and counter intuitive. Ive never used an API that ‘works’ like this.

GraphQL has a very similar issue with data/attributes nesting.

Our plans currently are to introduce additional glob filters e.g. *.* / ** to allow for wildcard nesting of deeper levels.

The best method for setting a default populate though was mentioned previously here, using a route middleware as they are quite simple to build.

Dynamic zones, components, and media fields are all relations in the backend and thus their population is treated exactly like all other relations (not populated by default)

2 Likes

I strongly agree with @pix3l and @callum-gander not only it might be kinda anoying to always explicitely specify all the fields to return, but also, and this is why I searched for this topic - as I’m used to take advantage of Open API schema to get types from, in combination with documentation plugin, all the fields are optional, which would in my case lead to having code full of ?.?.? and I will probably choose GraphQl after all, because in it’s case such field selection is by its design and one of the biggest advantages without any doubt…

On the other hand I realize that by strapi’s nature of creating the api’s dynamically this is probably the only way how to give the client the option of selecting what to fetch - model situation I wanna get only few fields for the list of articles, but all of it’s contents on it’s detail page. In case of relations, I would much rather see a cleaner REST pattern and having to call mutiple endpoints, or a combination of having the loaded to some level automatically, dunno :thinking:

I realize I’m just complaining without offering any ideas how to deal with this but I wanted to share my thoughts anyway… Perhaps implementing a way of providing some sort of views/dto/endpoint definitions in form of another json schemes? Just a quick thought…

Anyway, I love strapi so far and it’s very positive to see how live the community seems to be :100: :raised_hands:

EDIT: For the type generation purposes, it just occured to me, that maybe it might be up for developing more soffisticated generic interfaces that take the populate fileds into consideration

After posting my original middlware solution above I’ve found a potentially harmful side effect.

If I alter the ctx in that way for the book endpoint and then at some point later, I call the book-shops endpoint, the altered ctx is retained between calls.

This is fine if we’re just editing the populate array but I tried to use this mechanism to alter the page.pageSize for a single endpoint.

Subsequent requests that then define a limit fail as you can’t provide both a limit & pageSize together.

I’ve posted a Github issue to get to the bottom of whether that’s expected behaviour or not.

I’ve just started a new v4 project and run into this and im staggered… we’re overriding every controller that has a relation? jeez.

Is there a control on the admin panel im missing that allows you to enable population when creating the controller?

If no that’s my vote… i don’t want to customise every single controller for something as trivial as ths.