Strapi v4, No relation fields in User collection types

System Information
  • Strapi Version: 4.0.0
  • Operating System: Windows
  • Database: sqlite3 5.0.2
  • Node Version: v14.17.3
  • NPM Version: 6.14.13
  • Yarn Version:

Hi!
I am trying to add some relation fields to my
User (collection types, default strapi collection)

I created new collection types called User-Roles

I am able to retrieve them as an Authenticated user (USERS & PERMISSIONS PLUGIN)

{
    "data": [
        {
            "id": 1,
            "attributes": {
                "role_name": "simple-user",
                "createdAt": "2021-12-07T20:05:55.205Z",
                "updatedAt": "2021-12-07T20:05:56.259Z",
                "publishedAt": "2021-12-07T20:05:56.257Z"
            }
        },
        {
            "id": 2,
            "attributes": {
                "role_name": "admin-user",
                "createdAt": "2021-12-07T20:06:02.975Z",
                "updatedAt": "2021-12-07T20:06:03.458Z",
                "publishedAt": "2021-12-07T20:06:03.458Z"
            }
        }
    ],
    "meta": {
        "pagination": {
            "page": 1,
            "pageSize": 25,
            "pageCount": 1,
            "total": 2
        }
    }
}

And I am able to see them in Content manager

But when I am trying to fetch them I don’t see my relation field, and populate all doesn’t work

http://localhost:1337/api/users/me?populate=*

{
    "id": 2,
    "username": "test",
    "email": "test@test.test",
    "provider": "local",
    "confirmed": true,
    "blocked": false,
    "createdAt": "2021-12-07T19:52:40.305Z",
    "updatedAt": "2021-12-07T20:30:16.124Z"
}

I also don’t see them when login
http://localhost:1337/api/auth/local

2 Likes

If you want to see relations you need to use population, read docs.
But for some reason, this doesn’t work with the User collection type

1 Like

I think this might be an issue with permissions – by default quite a bit of the Users (roles / permissions) endpoint are disabled and will need to be updated before reading from them!

Any ideas of how to do this?
Because I can’t find anything in the documentation, or just was careful enough reading it.

Yes thank you, I somehow missed this.

I have been wondering the same thing.
Apparently the users/me route is not populeted by default, but i cant get the v3 solotion to work:

This PR probably can help.

Note: It is not merged yet.

Thanks!
Will check when will be merged and will reply here

You’re welcome!

I tried Strapi v4.0.2 and I still have the same issue.
No idea of how I can get my relations in /users endpoints.
Any ideas?

Hello!
It looks like the PR was not merged for 4.0.2.

In the mean time you can look at this: Fix unable to populate User in Users-Permissions by iicdii · Pull Request #11960 · strapi/strapi · GitHub

you can try to extend the user controller and manually merge the code changes.

I am not sure if it works but let me know!

Just wondering, Does this issue also includes the users/me not populating the roles in the record via REST API?

Because I am a little lost on how to do the frontend user authorization checks for example if I have made a custom “Support” role to show specific parts of the website and show a different one if its only “Authorized”

Sample

Just tried it and seems like it’s not helping, or maybe I did something wrong.
So I placed my extended file in
./src/extension/users-permissions/server/controllers/user.js
And the code from PR

'use strict';

/**
 * User.js controller
 *
 * @description: A set of functions called "actions" for managing `User`.
 */

const _ = require('lodash');
const utils = require('@strapi/utils');
const { convertPopulateQueryParams } = require('@strapi/utils/lib/convert-query-params');
const { getService } = require('../utils');
const { validateCreateUserBody, validateUpdateUserBody } = require('./validation/user');

const { sanitize } = utils;
const { ApplicationError, ValidationError } = utils.errors;

const sanitizeOutput = (user, ctx) => {
  const schema = strapi.getModel('plugin::users-permissions.user');
  const { auth } = ctx.state;

  return sanitize.contentAPI.output(user, schema, { auth });
};

module.exports = {
  /**
   * Create a/an user record.
   * @return {Object}
   */
  async create(ctx) {
    const advanced = await strapi
      .store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
      .get();

    await validateCreateUserBody(ctx.request.body);

    const { email, username, role } = ctx.request.body;

    const userWithSameUsername = await strapi
      .query('plugin::users-permissions.user')
      .findOne({ where: { username } });

    if (userWithSameUsername) {
      if (!email) throw new ApplicationError('Username already taken');
    }

    if (advanced.unique_email) {
      const userWithSameEmail = await strapi
        .query('plugin::users-permissions.user')
        .findOne({ where: { email: email.toLowerCase() } });

      if (userWithSameEmail) {
        throw new ApplicationError('Email already taken');
      }
    }

    const user = {
      ...ctx.request.body,
      provider: 'local',
    };

    user.email = _.toLower(user.email);

    if (!role) {
      const defaultRole = await strapi
        .query('plugin::users-permissions.role')
        .findOne({ where: { type: advanced.default_role } });

      user.role = defaultRole.id;
    }

    try {
      const data = await getService('user').add(user);
      const sanitizedData = await sanitizeOutput(data, ctx);

      ctx.created(sanitizedData);
    } catch (error) {
      throw new ApplicationError(error.message);
    }
  },

  /**
   * Update a/an user record.
   * @return {Object}
   */
  async update(ctx) {
    const advancedConfigs = await strapi
      .store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
      .get();

    const { id } = ctx.params;
    const { email, username, password } = ctx.request.body;

    const user = await getService('user').fetch({ id });

    await validateUpdateUserBody(ctx.request.body);

    if (user.provider === 'local' && _.has(ctx.request.body, 'password') && !password) {
      throw new ValidationError('password.notNull');
    }

    if (_.has(ctx.request.body, 'username')) {
      const userWithSameUsername = await strapi
        .query('plugin::users-permissions.user')
        .findOne({ where: { username } });

      if (userWithSameUsername && userWithSameUsername.id != id) {
        throw new ApplicationError('Username already taken');
      }
    }

    if (_.has(ctx.request.body, 'email') && advancedConfigs.unique_email) {
      const userWithSameEmail = await strapi
        .query('plugin::users-permissions.user')
        .findOne({ where: { email: email.toLowerCase() } });

      if (userWithSameEmail && userWithSameEmail.id != id) {
        throw new ApplicationError('Email already taken');
      }
      ctx.request.body.email = ctx.request.body.email.toLowerCase();
    }

    let updateData = {
      ...ctx.request.body,
    };

    const data = await getService('user').edit({ id }, updateData);
    const sanitizedData = await sanitizeOutput(data, ctx);

    ctx.send(sanitizedData);
  },

  /**
   * Retrieve user records.
   * @return {Object|Array}
   */
  async find(ctx) {
    const users = await getService('user').fetchAll(
      ctx.query.filters,
      convertPopulateQueryParams(ctx.query.populate || {})
    );

    ctx.body = await Promise.all(users.map(user => sanitizeOutput(user, ctx)));
  },

  /**
   * Retrieve a user record.
   * @return {Object}
   */
  async findOne(ctx) {
    const { id } = ctx.params;
    let data = await getService('user').fetch(
      { id },
      convertPopulateQueryParams(ctx.query.populate || {})
    );

    if (data) {
      data = await sanitizeOutput(data, ctx);
    }

    ctx.body = data;
  },

  /**
   * Retrieve user count.
   * @return {Number}
   */
  async count(ctx) {
    ctx.body = await getService('user').count(ctx.query);
  },

  /**
   * Destroy a/an user record.
   * @return {Object}
   */
  async destroy(ctx) {
    const { id } = ctx.params;

    const data = await getService('user').remove({ id });
    const sanitizedUser = await sanitizeOutput(data, ctx);

    ctx.send(sanitizedUser);
  },

  /**
   * Retrieve authenticated user.
   * @return {Object|Array}
   */
  async me(ctx) {
    const user = ctx.state.user;

    if (!user) {
      return ctx.unauthorized();
    }

    ctx.body = await sanitizeOutput(user, ctx);
  },
};

seems the fix is still not merged yet :frowning:

This content should properly be moved to ./src/extensions/users-permissions/strapi-server.js and be rewritten according to: Plugins extension - Strapi Developer Docs

I think this could be working then.

And additionally it seems like some changes are scheduled for the Version 4.0.5 (in the high hopes of it being merged): Fix unable to populate User in Users-Permissions by iicdii · Pull Request #11960 · strapi/strapi · GitHub