Strapi v4 Can't create or retrieve relation

System Information
  • Strapi Version: 4.1.8
  • Node Version: 16.13.1
  • NPM Version: 8.5.5

I’ve seen many posts on this issue, read the docs on new populate standard by quering. Tried many workarounds, no better results.

What I do get with new populate strategy is:

  • I overriden find and findOne, just so i could insert some logs and see what is going on
  • when populate=* or populate=fieldWanted query is passed, my console.log does show the fields on service level (api//services/.js), but it does not appear on controller level (api//controllers/.js).
  • on service level, I did yet another test:
async find(...args) {
    // Calling the default core controller
    const { results, pagination } = await super.find(...args);
    console.log('results:', results)
    for(var i = 0; i < results.length; i++){
      console.log('result'+i+':', results[i].users_permissions_user)
      results[i].user = results[i].users_permissions_user
    return { results, pagination };

Simply put, just changing the name of the field from users_permissions_user (the field I want) to simply user. This one (user) I receive!

  • same occours with yet anoter type different than user_permissions_user. Created a foo content and linked to the one I’m fetching, same result: if it is on query, I can see the value on services level, but it is not served back, just if the mentioned trick is applied.

There is something going on between services and controllers that erases that specific relation field.

Creating a field with a relation, the issue is similar. I send post to api/my-content with something like

  "value1": true,
  "value2": false,
  "users_permission_user": 2

receive result 200 but the relation is not created. I did not find out how the query is sent from controllers.create to services.create, but when it reaches services I can see the data, but the relational field is already erased.

Any thoughs?

Alright, took a deep breath and continued console.log investigation.

the definition of
on node_modules/@strapi/strapi/factories.js calls (on line 11)
node_modules/@strapi/strapi/core-api/controller/index.js which calls (on line 24)

return sanitize.contentAPI.output(data, contentType, { auth });

defined on
node_modules/@strapi/strapi/utils/sanitize/index.js which contains, finally

output(data, schema, { auth } = {}) {
      if (isArray(data)) {
        return Promise.all( => this.output(entry, schema, { auth })));

      const transforms = [sanitizers.defaultSanitizeOutput(schema)];

      if (auth) {
        // this line seems the solution
        transforms.push(traverseEntity(visitors.removeRestrictedRelations(auth), { schema }));

      console.log('here data is still the same, I have the fields I want:', data)
      return pipeAsync(...transforms)(data);
      // after this return, data I want is lost.

After this deep dive, I found that line with removeRestrictedRelations(), which seems to setup transforms variable. pipeAsync seems to be just a “create a function which applies this set of functions defined by transforms to some entering data and return” thing.

By this function name (removeRestrictedRelations) I double checked if the user role which is trying to access data (Public role) has this permition, and it has. So, it does not seems to be related to role permissions.

Now, regarding data fetch, my question is… what exactly is this Relation restriction and how can I setup / inform in my request that these fields are not restricted? I did not see anything like that on the docs.

Will do the same kind of investigation on create-entry-with-relation problem.


Just commented sanitize line in both find and create controllers, it worked. Create with relation and retrieve with relations. There is something going on on sanitization process. Can be a misconfiguration on my part (probably? can’t tell from reading the docs) or some bug.

For people finding this thread:

So, when you do a populate call to a controller, the request passes through Role-Based Access Control (RBAC). The response of the controller is then sanitised before being returned. If you make your request by doing something like super.find(ctx) where you’ve modified some part of the ctx to include a relation field.

Now, if you don’t really care (i.e. you think that the populate shouldn’t be being sanitised) you can add the following:

ctx.state.auth = null

If no auth object exists in the state, sanitising relations based on RBAC is skipped.