Users me update

Hello,

How do I add the “PUT” methode to the users/me api?

I’ve already tried the online tutorial Update your own user data - YouTube
but the files he is referring to in his git don’t exist anymore (I guess it’s obsolete since it’s from 2020), and I tried to implement in locally but it doesn’t work.

Or any way to restrict “PUT” method on the /users/:id api to only the user itself?

Thank you for your answers!

2 Likes

I did an example about 2 months ago for creating a custom updateMe route, can’t promise it’s up to date or works now but: GitHub - derrickmehaffy/strapi-example-v4-customUpdateMe: Example Strapi app showing how you can add a PUT /api/user/me

6 Likes

I customized the users-permissions plugin this way:

// src/extensions/users-permissions/strapi-server.js
// this route is only allowed for authenticated users

module.exports = (plugin) => {

  /*******************************  CUSTOM CONTROLERS  ********************************/
  plugin.controllers.user.updateLoggedInUser = async (ctx) => {
    await strapi.query('plugin::users-permissions.user').update({
      where: {id: ctx.state.user.id},
      data: ctx.request.body.data
    }).then((res) => {
      ctx.response.status = 200;
    })
  }

  /*******************************  CUSTOM ROUTES  ********************************/
  plugin.routes["content-api"].routes.push(
    {
      method: "POST",
      path: "/user/updateLoggedInUser",
      handler: "user.updateLoggedInUser",
      config: {
        prefix: "",
        policies: []
      }
    }
    })

  return plugin;
};

There is the check against the ctx.state.user.id but i am unsure if this is secure (enough)?

4 Likes

I would do a check before you do the update to make sure the user.id exists but other than that it looks fine.

1 Like

Thanks DMehaffy’s answer. I tried few times but no luck. Got 405 Error
I am using Strapi 4.4.5 and this is my answer.

const _ = require('lodash')
const user = require('./content-types/user');
module.exports = plugin => {
  // PUT updateMe is not allowed, so use /users/:id
  plugin.contentTypes.user = user;
  plugin.controllers.user.update = async (ctx) => {
    const theLoggedInUserId = ctx.state.user.id
    const theUserId = +ctx.params.id
    if(theLoggedInUserId !== theUserId){
      return ctx.badRequest('forbidden to update other user')
    }
    // Reconstruct context so we can pass to the controller
    const newData = _.pick(ctx.request.body.data, ['password', 'avatar']);
    ctx.request.body.data = newData
    return await strapi.entityService.update(
      'plugin::users-permissions.user',
      ctx.params.id,
      {
        data: ctx.request.body.data
      }
    );
  };
  return plugin;
};

Please specify more details about your solution. It’s out of context and is not clear how to make it work in my case. A repo would be best

Is there really no way still to update the users own account without exposing the users/:id update path? I’m not sure if that’s an oversight in Strapi development, but it’s really blocking my progress with strapi. The JS repository does not work for me. The route gets registered but I always get 403 even with a valid jwt

at the end of 2022, this solution worked for me:

module.exports = (plugin) => {
  // custom controller
  plugin.controllers.user.updateMe = async (ctx) => {
    if (!ctx.state.user || !ctx.state.user.id) {
      return ctx.response.status = 401
    }

    await strapi.query('plugin::users-permissions.user').update({
      where: { id: ctx.state.user.id },
      data: ctx.request.body
    }).then(res => {
      ctx.response.status = 200
    })
  }

  // custom route
  plugin.routes["content-api"].routes.push(
    {
      method: "PUT",
      path: "/user/me",                         
      handler: "user.updateMe",
      config: {
        prefix: "",
        policies: []
      }
    })

  return plugin
}

to avoid conflict with ‘users/me’, use another path. for example i used ‘user/me’

I created an example for this a little while back now but it should still function:

I’m specifically doing this via the extensions system in v4:

2 Likes

Yes, I really believed that in strapi 4 these essential needs should be prioritized and integrated into strapi, without having to program it as an extension.

I see what you are saying but it would say the best solution currently is making an extension for a path users/me and as method put then I would just make a middleware what sets the ctx.state.user.id to ctx.params.id and then just use as the handler user.update

what does this do the middleware sets the the id in user/:id and then just sends the request to the update controller update uses this would most likely give you the least amount of issues/ maintenance in the long run

I will most likely write a quick plugin later today and push it to github that does this for you. since I think that that is currently the best solution to this problem.

Update talked to derrick and he asked me to implement it in users-permissions

hi, like your code work in 4.6.1.

But in 4.6.2 version (last) i have a error:

Cannot find module ‘request’

module.exports = (plugin) => {
  // custom controller
  plugin.controllers.user.updateMe = async (ctx) => {
    if (!ctx.state.user || !ctx.state.user.id) {
      return ctx.response.status = 401
    }

    await strapi.query('plugin::users-permissions.user').update({
      where: { id: ctx.state.user.id },
      data: ctx.request.body.data
    }).then(res => {
      ctx.response.status = 200
    })
  }

  // custom route
  plugin.routes["content-api"].routes.push(
    {
      method: "PUT",
      path: "/user/me",                         
      handler: "user.updateMe",
      config: {
        prefix: "",
        policies: []
      }
    })

  return plugin
}

This is a great response. My only gripe with this is that it does not return errors/response in the same way as other endpoints do. Like, For register endpoint, for example, has this response. ok or response.error.details for error details.

Users and permissions is a v3 code that has been translated with a v4 compatibility layer that is why u&p is so wired