Adding relational data to the user model / collection in the ctx.state.user

System Information
  • Strapi Version: v3.1.4
  • Operating System: Mac OS X Catalina
  • Database: MongoDB (Locally hosted)
  • Node Version: 12.17.0
  • NPM Version: 6.14.4
  • Yarn Version: 1.22.5

Hi there,

My user model contains a few additional relational fields that I would like to be available and populated when querying ctx.state.user.

Currently the ctx.state.user returns:

{
  isActive: true,
  blocked: false,
  roles: [
    {
      _id: 5f52761848053351565e8c1f,
      name: 'Super Admin',
      code: 'strapi-super-admin',
      description: 'Super Admins can access and manage all features and settings.',
      createdAt: 2020-09-04T17:15:04.302Z,
      updatedAt: 2020-09-04T17:15:04.302Z,
      __v: 0,
      id: '5f52761848053351565e8c1f'
    }
  ],
  _id: 5ec9195ae968249eafb6d5ab,
  username: 'stephan@test.com',
  password: '$2a$10$AJjVSG6SPSqA0z0Npq7vduNJ1SYBeppaHxRhMRzqirI1C9pRvvDB.',
  email: 'stephan@test.com',
  createdAt: 2020-05-23T12:38:50.372Z,
  updatedAt: 2020-09-10T12:10:38.806Z,
  __v: 0,
  firstname: 'Stephan',
  lastname: 'Smith',
  id: '5ec9195ae968249eafb6d5ab'
}

The actual user model looks as follow:

How do I update the ctx.state.user to reflect the data from the “isps” relation model?

The response I hope to get will look something like this:

{
  isActive: true,
  blocked: false,
  roles: [
    {
      _id: 5f52761848053351565e8c1f,
      name: 'Super Admin',
      code: 'strapi-super-admin',
      description: 'Super Admins can access and manage all features and settings.',
      createdAt: 2020-09-04T17:15:04.302Z,
      updatedAt: 2020-09-04T17:15:04.302Z,
      __v: 0,
      id: '5f52761848053351565e8c1f'
    }
  ],
  _id: 5ec9195ae968249eafb6d5ab,
  username: 'stephan@test.com',
  password: '$2a$10$AJjVSG6SPSqA0z0Npq7vduNJ1SYBeppaHxRhMRzqirI1C9pRvvDB.',
  email: 'stephan@test.com',
  createdAt: 2020-05-23T12:38:50.372Z,
  updatedAt: 2020-09-10T12:10:38.806Z,
  __v: 0,
  firstname: 'Stephan',
  lastname: 'Smith',
  id: '5ec9195ae968249eafb6d5ab',
  isp: {  // <------- The ISP collection included
     _id: 5e234829034823094,
      name: 'Mweb',
      vlan: 200
  }
}

Appreciate any assistance.

I’ll provide three valid metods. I would recommend to get it inside controller at the first step.

1. Inside controller(recommended)

let { isp } = await strapi.query('user', 'users-permissions').findOne({ id: ctx.state.user.id }, ['isp']);
ctx.state.user.isp = isp;

2. By using policies(also recommended), will work the same as the controller method

Create a file under ./config/policies/includeUserRelations.js

With the following code:

module.exports = async (ctx, next) => {
  if (ctx.state.user) {
    let { isp } = await strapi.query('user', 'users-permissions').findOne({ id: ctx.state.user.id }, ['isp']);
    ctx.state.user.isp = isp;
  }
  return await next();
};

Now add that policy to your route where you want to get isp under ctx.state.user object.
Example:

[
{
      "method": "GET",
      "path": "/orders",
      "handler": "order.find",
      "config": {
        "policies": ["global::includeUserRelations"]
      }
    }
]

Check policies documentation if you want to create API scoped policy.


3. Using extensions(not recommended) - it will be hard to maintain future updates

Create a file under:
./extensions/users-permissions/config/policies/permissions.js

Copy inside it content from:
./node_modules/strapi-plugin-users-permissions/config/policies/permissions.js

Replace this code:
ctx.state.user = await strapi.plugins['users-permissions'].services.user.fetchAuthenticatedUser(id);

With this one:
ctx.state.user = await strapi.query('user', 'users-permissions').findOne({ id }, ['isp']);

4 Likes

Thanks @sunnyson,

I tried both option 1 and 2 with no success, however I think its something minor.

When using option 1, I am presented with the following error:

[2020-11-14T04:32:27.154Z] error MissingSchemaError: Schema hasn't been registered for model "user".
Use mongoose.model(name, schema)
    at new MissingSchemaError 

When using option 2:
I see no difference. For example when I query ctx.state.user in a custom controller, the ISP detail is not included.

What am I doing wrong?

Nevermind, I found the issue. The “ISP” collection returned an array and it is also called “isps”.

Solution option 1 - worked just perfectly!
Solution option 2 - works as well. Initially forgot to add the change in the routes. Now added and works like a charm!

Thanks so much @sunnyson

1 Like

Hi, I am facing the same problem as @Stephan_Du_Toit. For method 1, which controller needs to be updated such that the additional fields get exposed everywhere when querying ctx.state.user?

Hi @p-gw
Apologies, I never got a notification for your post and just happen to see it when I logged on.
Did you ever manage to resolve your issue?

The fields you want to expose, are probably part of some collection. In my case, it was the “isps” collection. Therefore you need to edit that controller.
I did however opted for Option 2 - Policies.

Herewith a screenshot. Maybe it helps.

!

Any update on how to include user relations in ctx.state.user for strapi v4? @sunnyson

Nevermind, I’ve figured it out :slight_smile: .

In ./src/extensions/user-permission/strapi-server.js
I added:

module.exports = (plugin) => {
  plugin.controllers.user.me = async (ctx) => {
    const id = ctx.state.user.id;
    return await strapi.entityService.findOne(
      "plugin::users-permissions.user",
      id,
      { populate: { EXAMPLE: true } }
    );
  };
};

This makes it so that my /api/users/me endpoint returns the user object with the EXAMPLE relational field included.

1 Like

Hi, I tried this but this isn’t adding the field to my ctx.state.user
I added this exact code but replaced it with the field I have