Content migrated from: https://github.com/strapi/strapi/discussions/6871
Original Post
I’ve a requirement to store the user id from multiple content types and was looking for a way to use lifecycle hooks: afterCreate
, afterUpdate
, afterDelete
.
Looks like the lifecycle hook arguments only returns data from the specific model (entity and request params) but is there a way to access ctx.state.user
from these functions?
I’ll appreciate all feedback! … Thanks!
Comments - Thread 1
Collaborator
@alexandrebodin is ctx
no longer passed to lifecycle callbacks? (I know it was previously before the changes made in stable).
If not, was there a specific reason we aren’t passing the full ctx over, or is it just not documented?
Author
I even tried this:
async afterCreate(result, data) {
strapi.app.use(async ctx => {
ctx; // is the Context
console.log("ctx.request", ctx.request);
console.log("ctx.response", ctx.response);
});
console.log(strapi.services.history.ACTIONS.CREATED);
console.log('afterCreate', result, data);
},
With no luck, is there any way to get the ctx
object inside a hook?
Maintainer
Hi, the ctx is on the HTTP JSON API layer it is not possible to pass it down to the DB lifecycle layer automatically. The db layer can be called from anywhere and is not related to any current query.
What you can do is extend your controllers to update the input data with the ctx.state.user if you wish to.
FYI @derrickmehaffy it wasn’t in the previous lifecycles we just changed
Collaborator
I’m thinking we should at least pass down the state though like ctx.state.user
as this is going to be a fairly common usecase I think (considering that lifecycles are called not only via the generated routes but also the admin) assuming that the admin also has a ctx.state.user which I have never really looked at.
Collaborator
Looking through the old docs from beta.19.5 I guess the other options were never really clearly defined as to what they did beforeCreate: async (model, attrs, options)
Maintainer
DB lifecycles will not get request context. this should be handled by a layer an top of those lifecycles like a request lifecycle that will set those informations before calling the DB layer.
Collaborator
DB lifecycles will not get request context. this should be handled by a layer an top of those lifecycles like a request lifecycle that will set those informations before calling the DB layer.
Are you hinting towards controller lifecycles the main reason I suppose why I try to avoid directing people to controller modification is for updates. Though not really a larger problem now as it was in the Alpha/Beta days where those would change often. Old habits die hard I suppose.
Comments - Thread 2
Contributor
Is there any update on this issue?
For example, I want to send an email after User is created.
But the request should return error when sending email has failed.
Collaborator
We do not have an update currently
Contributor
I solved it by creating custom controller method.
Thank you anyway
Comments - Thread 3
This is a “dupe” of How to get context or Current user in Function beforeSave · Issue #3309 · strapi/strapi · GitHub
Honestly, with strapi many times you find yourself reimplementing controllers with few lines of code and therefore I don’t see any problem with this issue in particular honestly.
Just replace your create and then call strapi.services.model.create({ userObj })
passing in whatever object you want to have available in your lifecycle hook.
A slightly extended snippet:
module.exports = {
async create(ctx) {
const body = ctx.request.body;
const user = ctx.state.user;
if (!user) {
return ctx.badRequest(null, [
{ messages: [{ id: "No authorization header was found" }] },
]);
}
const entity = await strapi.services.note.create({
description: body.description,
user: user,
});
return sanitizeEntity(entity, { model: strapi.models.note });
},
};
and then in the model
module.exports = {
lifecycles: {
async beforeCreate(data) {
console.log(data.userObj);
}
},
};
Just encountered a similar problem (where i want to auto-fill created_by and updated_by with end-user ids via the “Users & permissions” plugin)
I guess this is the best solution, but with this i can’t stay DRY, instead i have to implement each controller (even if they’re just plain CRUD controllers) and always pass ctx.state.user
manually.
Maybe instead of using a global strapi.services.note.create
we would have to have some sort of services-object which contains functions that are bound to the ctx.state
passed to the actual controller, e.g.
async create(ctx, services) {
services.note.create(ctx.request.body)
...
}
Maybe some sort of controller middleware could do this by leveraging the Proxy API, so that when calling services.note.create
the state would be passed automatically as a second parameter to strapi.services.note.create
I realize that this is quite the large change, and there are probably better ways to do it, but currently it seems very inflexible
Collaborator
@alexandrebodin @Convly this was what I mentioned as well regarding some of the user feedback I’ve seen in slack. When we added RBAC we assume that all content is created in the admin, but in many users cases it could be coming from the user APIs.
Everyone else, I’ve created an actual feature request here: #8193
This is a topic we have discussed internally as well and quite a few of us internally agree about the topic. (Passing ctx into the lifecycles)