28

I decided today that I'm going to use Strapi as my headless CMS for my portfolio, I've bumped into some issues though, which I just seem to not be able to find a solution to online. Maybe I'm just too clueless to actually find the real issue.

I have set up a schema for my projects that will be stored in Strapi (everything done in the web), but I've had some issues with my custom components, and that is, they are not part of the API responses when I run it through Postman. (Not just empty keys but not included in the response at all). All other fields, that are not components, are filled out as expected.

At first I thought it might have to do with the permissions, but everything is enabled so it can't be that, I also tried looking into the API in the code, but that logging the answer there didn't include the components either.

Here is an image of some of the fields in the schema, but more importantly the components that are not included in the response.

Some of the fields

So my question is, do I need to create some sort of a parser or anything in the project to be able to include these fields, or why are they not included?

ouflak
  • 2,458
  • 10
  • 44
  • 49
Sandmountain
  • 447
  • 1
  • 5
  • 13

9 Answers9

58

I had the same problem and was able to fix it by adding populate=* to the end of the API endpoint.

For example:

http://localhost:1337/api/test?populate=*

More info here: https://forum.strapi.io/t/cannot-see-media-field-in-my-endpoint-for-my-content-type/13082/2

edit: Only first-level relations are populated with populate=* . Use the LHS bracket syntax (i.e. [populate]=*) to populate deeper:

For example:

http://localhost:1337/api/test?populate[TestExamples][populate]=*

More info here if you go to Relations population: https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest-api.html#fields-selection

IceJonas
  • 720
  • 6
  • 13
  • can i sort the populated data with createdAt ascending and descending order? I want recent data first. http://localhost:1337/api/categories/1?fields=title&populate=articles&sort=createdAt:desc – Sushilzzz Dec 15 '21 at 02:28
  • Thank you, I spend my morning to find this solution, I'm new in Strapi – Aymane Lassfar Dec 27 '21 at 14:36
  • @IceJonas thank you very mich! this saved my day ^-^ – SupaMario Jan 18 '22 at 12:49
  • Note this doesn't populate relational fields on components. – James Parker Feb 08 '22 at 16:00
  • Which can be done like so api/jams/5?populate[fieldName][populate]=* – James Parker Feb 08 '22 at 16:13
  • This is great, but I need one more filter, for example for TestExamples I want only one locale, where to put, for example, ?locale=en ? I tried everywhere but I don't know where to put – Vesko_dev Jul 11 '22 at 10:12
  • @Vesko_dev have you tried using &locale=en? Often the first param is ? and everything after that is &. No idea if it will solve your problem but you could try. – IceJonas Jul 12 '22 at 15:16
  • you just saved my day ! :D – PayamB. Oct 30 '22 at 09:27
  • Ahhh but what if you have a combination of 3 relations/components but you only want two of them returned... how do you do that? I tried variations around ?populate[fieldName1][fieldName2][populate]=* but nothing worked so far. If I use ?populate=* I get all 3 fields returned. – JeremyW Jan 17 '23 at 03:01
  • Nvm got it haha... ?populate[Latest][populate]=*&populate[Movies][populate]=*. Feels like that should be ?populate[Latest]=*&populate[Movies]=* but *shrug* – JeremyW Jan 17 '23 at 03:06
8

Just to add to the accepted answer, if you are working with a lot of nested collections or nested components (for example using a lot of custom components in Single Types), then it might be worth to write a custom controller.

I need to render a homepage which has a 'Header' component, and the Header has two nested 'Button' components which are repeatable. Additionally, I want to render a collection of 'Clients' which is a one-to-many relationship.

Since this data structure won't change often, I have just written a custom controller so I don't have to worry about this in the front-end.

Let's say you have created a Homepage Single Type to render your homepage, the controller will be available in the following directory: /src/api/homepage/controllers/homepage.js

In the below example I want to render the Header component with the nested header_image and buttons and the Clients component with the nested clients collection.

const { createCoreController } = require('@strapi/strapi').factories;

module.exports = createCoreController('api::homepage.homepage', ({ strapi }) => ({
    async find(ctx) {
        const populateList = [
            'Header.header_image',
            'Header.buttons',
            'Clients.clients'
        ]
        // Push any additional query params to the array
        populateList.push(ctx.query.populate)
        ctx.query.populate = populateList.join(',')

        const content = await super.find(ctx)
        return content
    }
}));

There seems to be a significant difference between Strapi V3 and V4 and I find the documentation on populating nested components quite poor for V4. I guess this will be updated in the next couple of weeks as Strapi V4 has only just been released.

If there is a better way to populate deeply nested components or relationships I'd love to hear but I hope that the above also helps some people in the meantime.

BleddP
  • 181
  • 7
  • can i sort the populated data with createdAt ascending and descending order? I want recent data first. http://localhost:1337/api/categories/1?fields=title&populate=articles&sort=createdAt:desc – Sushilzzz Dec 15 '21 at 03:21
  • how do i sort my data using above code ? – Sushilzzz Dec 15 '21 at 03:22
3

I'm relatively noob to Strapi, especially V4. I have only developed one app with version 3.6.8 in the last year. Unless you haven't seen a problem yet, I believe I solved that issue in the endpoint call. I must also say that I have not deployed it yet. It's just in the development environment.

The data architecture

The endpoint call:

http://localhost:1337/api/profiles/?filters[user]=1&populate[0]=permissions&populate1=permissions.forms.form&populate2=permissions.quiz&populate[3]=permissions.unity&populate[4]=permissions.forms.form.Questions Blockquote

The Endpoint Call

Dharman
  • 30,962
  • 25
  • 85
  • 135
1

whats even worse is that you cant filter Strapi Components with Graphql..

Dennis Zab
  • 33
  • 3
1

Another solution for this problem is to use a npm package called: Strapi plugin populate-deep

install it in the backend folder (strapi) and in the frontend (I use next.js) use it like so:

http://localhost:1337/api/pages?populate=deep

the default max depth level is 5, you can override it either with:

http://localhost:1337/api/pages?populate=deep,6

or add the following code in config/plugins.js in backend folder (add this file if it does not exist):

module.exports = ({ env }) => ({
  'strapi-plugin-populate-deep': {
    config: {
      defaultDepth: 6, // Default is 5
    }
  },
});
Nathan Barel
  • 348
  • 2
  • 14
0

Gatsbyjs solution!!!: I am using single types and I have a component inside of that single type called for example header settings where users fill stuff like phone number location and so on... and I am also using Gatsbyjs for my front-end and here strapi.js Screenshot is a solution for that too.gatsby-config.js Screenshot Just in case someone has the same issue

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 14 '22 at 11:44
0

For people using Angular, it can be quite annoying to create the correct nested QueryParams to use with the HttpClient so I created this DeepQuery class (gist here, in case of issues):

class DeepQuery {
  constructor(public query: any) {

  }

  private isObj(obj: any): boolean {
    return (
      typeof obj === 'object' &&
      obj !== null
    )
  }

  private toQPK(crumbs: string[]): string {
    if (crumbs.length == 0) {
      throw new Error(`Parameter without key`)
    }
    return crumbs[0] + crumbs.slice(1).map(s => `[${s}]`).join("");
  }

  private parse(obj: any, crumbs: string[] = [], memo: {[key: string]: any} = {}) {
    if (!this.isObj(obj)) {
      memo[this.toQPK(crumbs)] = obj;
    } else if (Array.isArray(obj)) {
      for (let i = 0; i < obj.length; i++) {
        crumbs.push(i.toString());
        this.parse(obj[i], crumbs, memo);
        crumbs.pop();
      }
    } else {
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          crumbs.push(key);
          this.parse(obj[key], crumbs, memo)
          crumbs.pop();
        }
      }
    }
    return memo;
  }

  public encode() {
    return this.parse(this.query);
  }
}

Its encode method creates an object that you can pass to any HTTP calls you make:

makeCall(type: string): Observable<any> {
    let populate = {
        authors: ["listing", "publisher"]
    };
    let params = new DeepQuery({populate});
    return this.html.get<any>(url.href, params.encode());
  }
Robin De Schepper
  • 4,942
  • 4
  • 35
  • 56
0

i found this solution with the Strapi plugin to be able to fetch the data of a collectionTypes that have inside a custom component create with Strapi.

To be clearly the my situation is this one, i have a collectionType named "article" that have inside a custom component, and to fetch them apparently we need to deep dive.

here is my solution with the strapi plugin gatsby-source-strapi

{
  resolve: "gatsby-source-strapi",
  options: {
    apiURL:
      process.env.NODE_ENV === "production"
        ? process.env.API_URL
        : "http://localhost:1337/api",
    accessToken: process.env.STRAPI_TOKEN,
    collectionTypes: [
      { name: `descrizioni-home-page` },
      { name: "article", endpoint: "articles?populate=*" },
    ],
    singleTypes: [{ name: "homepage", endpoint: "homepage?populate=*" }],
    queryLimit: 5000,
  },
},
0

This seem to work for me when getting dynamic components and components in Strapi v4, not sure if it is the "recommended way", 3 ways (1 and 2 are almost same)

1)

`${process.env.NEXT_PUBLIC_STRAPI_API}/pages?publicationState=live&populate[seo][populate]=%2A&populate[pageHeading][populate]=%2A&populate[socialMedia][populate]=%2A&populate[components][populate]=%2A&filters[slug]=${params.slug}`

Same as 1) except replace "%2A" with "*":

`${process.env.NEXT_PUBLIC_STRAPI_API}/pages?publicationState=live&populate[seo][populate]=*&populate[pageHeading][populate]=*&populate[socialMedia][populate]=*&populate[components][populate]=*&filters[slug]=${params.slug}`
  1. ${process.env.NEXT_PUBLIC_STRAPI_API}/pages?publicationState=live&filters[slug]=${params.slug}

controller (src/api/page/controllers/page.js)

"use strict";

/**
 *  page controller
 */

const { createCoreController } = require("@strapi/strapi").factories;

module.exports = createCoreController("api::page.page", () => ({
  async find(ctx) {
    const populateList = [
      "seo",
      "pageHeading",
      "socialMedia",
      "components.image",
      "components.types",
    ];
    populateList.push(ctx.query.populate);
    ctx.query.populate = populateList.join(",");

    const content = await super.find(ctx);
    return content;
  },
}));

enter image description here

enter image description here

atazmin
  • 4,757
  • 1
  • 32
  • 23