How can I create user lifecycle hooks in v4?

I’m updating a project to v4 today and things have been relatively smooth sailing, but this is the last piece of the puzzle.

How can I create lifecycle hooks for the User model?

In v3 I had this defined in the extensions/user-permissions/models/User.js file.

Following intuition based on patterns elsewhere in v4 I tried to add it in src/extensions/user-permissions/content-types/user/lifecycles.js but this does not seem to work.

1 Like

I’m also looking for the answer to this.

I’ve tried various ways inside src/extensions/user-permissions/strapi-server.js. And I’ve tried using strapi.db.lifecycles.subscribe() from this documentation but no luck

I figured out a workaround for now, turns out I was using the incorrect model name, so the db lifecycle hook does work for the user model (docs)

In src/index.js, this is my code, I wanted to send a confirmation email after user create:

'use strict';

module.exports = {
  /**
   * An asynchronous register function that runs before
   * your application is initialized.
   *
   * This gives you an opportunity to extend code.
   */
  register(/*{ strapi }*/) {},

  /**
   * An asynchronous bootstrap function that runs before
   * your application gets started.
   *
   * This gives you an opportunity to set up your data model,
   * run jobs, or perform some special logic.
   */
  async bootstrap({ strapi }) {
    strapi.db.lifecycles.subscribe({
      models: ['plugin::users-permissions.user'],

      async afterCreate(event) {
        await strapi.service('plugin::users-permissions.user').sendConfirmationEmail(
          event.result
        );
      },
    });
  },
};

This solution doesn’t work as well for beforeCreate, as this hook is before the entity is created in the database, whereas you might want logic to run before any of the user-permissions plugin code.

To get around this, I modified the create controller by using the strapi plugin extension.

Here’s the code in this file: src/extensions/user-permissions/strapi-server.js. This is defaulting the user’s username and password so they don’t have to be filled in from the admin front end

const crypto = require('crypto');

module.exports = (plugin) => {
  const userCreate = plugin.controllers.contentmanageruser.create;

  plugin.controllers.contentmanageruser.create = async (ctx) => {
    ctx.request.body.username = ctx.request.body.email;
    ctx.request.body.password = crypto.randomBytes(64).toString('hex');
    ctx.request.body.confirmed = false;

    await userCreate(ctx);
  };

  return plugin;
};
5 Likes

Thank you, Kieran!

Just using the database layer API to register the subscriber in the bootstrap function worked well for my use case.

1 Like

This other solution provided by @Robert_Hlavica also works very well Strapi create new User (users-permissions plugin), lifecycles - #6 by Robert_Hlavica

1 Like

This is strange i am using 4.5.2 and `const userCreate = plugin.controllers.contentmanageruser.create;

plugin.controllers.contentmanageruser.create = async (ctx) => {
console.log(“register called”);

await userCreate(ctx);

};`
is not getting called.

I basically want to add the user in another content as a reference on creation. Like auto add the user to another table. How do i do this ?

Thanks a ton!

One year later, this solution doesn’t work anymore either. Creating src/extensions/user-permissions/strapi-server.js and adding the described logic unfortunately doesn’t do anything.

Wherever you look for lifecycle hooks for user data, the only working option that is left seems to be subscribing to the db operations.

The issue is: How do I access the request details in there? The things that ctx contains. Since at least 2 years people try to get proper lifecycle hooks for user data. come on guys, this needs to get working somehow finally.

1 Like

The extensions should still work I just think the code is outdated and newer versions use a different controller name

also about your issue you can do strapi.requestContext.get(); from anywhere inside of strapi in v4.3.9+
Still if you would want more data then a lifecycle provides like relationships you would have to add custom middleware’s to all admin and api routes.

Hello,

Thanks for answer, works for me!

Regards,

I will save this for future purposes, thanks!

So as of 2024 the solution to this is the same but the controller you use is different.

Here is the updated code:

// /src/extensions/user-permissions/strapi-server.ts
export default function (plugin) {
  const registerController = plugin.controllers.auth.register;
  plugin.controllers.auth.register= async(ctx)=> {
    const body = ctx.request.body

    // do some modifications
    body.username = Math.random()

    await registerController(ctx)
  }

  return plugin;
}

If you want to create a user with a role, you can update the user after:

// /src/extensions/user-permissions/strapi-server.ts
export default function (plugin) {
  const registerController = plugin.controllers.auth.register;
  plugin.controllers.auth.register= async(ctx)=> {
    const body = ctx.request.body

    // do some modifications
    body.username = Math.random()

    await registerController(ctx)

    // bind role
    const user = ctx.response._body.user;
    const updatedUser = await strapi.entityService.update("plugin::users-permissions.user", user.id, {
      data: {
        role: ctx.request.body.role
      },
      populate: {role: true}
    })

    user.role = updatedUser.role;
  }

  return plugin;
}

^^ with this you can pass the role in the request body as: role: 4

3 Likes

Thank you for sharing this.

1 Like