[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add model event listeners to execute code before or after inserting/updating models #22533

Open
MichaelNZ85 opened this issue Jan 1, 2024 · 2 comments
Labels
domain/client Issue in the "Client" domain: Prisma Client, Prisma Studio etc. kind/feature A request for a new feature. topic: clientExtensions topic: `query` Client Extension

Comments

@MichaelNZ85
Copy link

Problem

There is no simple way to listen to lifecycle events on models (creating, updating etc). This is possible in TypeORM with entity listeners/subscribers such as @BeforeInsert and in Laravel with Eloquent events.

Suggested solution

Allow users to listen to lifecycle events, either in the schema or in the Prisma client. Something like this (adding a standard user role to a newly created user):

onUserCreated: async (user: User): Promise<void> {
  const standardUserRole = await prisma.role.findUnique({ where: { name: "standard_user"}});
  await prisma.user.update({
  where: {
    id: user.id,
  },
  data: {
    roles: {
      connect: {
        id: standardUserRole.id,
      },
   },
},
})
}

Alternatives

Custom functions could be created to accomplish this, but it would be easier if every time a model is created or updated, the necessary code is run.

Prisma is great, but without this feature I will not be using it.

@jkomyno
Copy link
Contributor
jkomyno commented Jan 2, 2024

Hi @MichaelNZ85, this should already be possible via Prisma's $extends() (link to the docs).

The idea is that you can create wrappers for certain operations within Prisma itself, so you can add your own custom logic before or after Prisma executes that query.

You can add logs, modify Prisma filters, invoke async operations tied to your own business logic, and so on.

Please give it a try and let us know whether this information is enough to close this issue :)
Thanks!

@jkomyno jkomyno added kind/improvement An improvement to existing feature and code. domain/client Issue in the "Client" domain: Prisma Client, Prisma Studio etc. topic: clientExtensions topic: extension/query labels Jan 2, 2024
@janpio janpio added kind/feature A request for a new feature. and removed kind/improvement An improvement to existing feature and code. labels Feb 16, 2024
@vgarmash
Copy link
vgarmash commented Jul 4, 2024

Hi @MichaelNZ85, this should already be possible via Prisma's $extends() (link to the docs).

The idea is that you can create wrappers for certain operations within Prisma itself, so you can add your own custom logic before or after Prisma executes that query.

You can add logs, modify Prisma filters, invoke async operations tied to your own business logic, and so on.

Please give it a try and let us know whether this information is enough to close this issue :) Thanks!

I gave it a try and found that it is not possible to create @AfterInsert event listener. In all docs you can see examples of executing something before query(args). I tried to overcome this by doing the following (simplified version, not real thing):

export async function onUserCreated(user_id: string, args: Prisma.userCreateArgs) {
   const newUser = await prisma.user.findFirstOrThrow({
        where: {
            user_id: user_id
        }
    });
   await calculateUserStatus(newUser); //some complex logic there
}
const prisma = new PrismaClient().$extends({
  query: {
    user: {
      async create({ model, operation, args, query }) {
        const result = await query(args);
        await onUserCreated(result.user_id, args); //this function will succeed 
                                                  //only if the record has been saved into DB 
                                                  //and the transaction has been committed
        return result;
      },
    },
  },
})

Then I tried the following:

const prisma = new PrismaClient().$extends({
  query: {
    user: {
      async create({ model, operation, args, query }) {
        let create_result: any;
        return query(args)
                 .then((result) => {
                       create_result = result1;
                       return onUserCreated(result.user_id, args)
                          .then(() => {
                              return create_result;
                            }) 
                });
      },
    },
  },
})

Both variants failed to find the user we just created in the DB and the calculation on it is impossible.

Although I found that prisma does not commit the transaction until the extended create function returns the result. Therefore, $extends() can't be used to create listeners that should be executed only after the record has been created in the database. We have to, as you said, "invoke async operations tied to your own business logic" in separate thread based on hope that the final transaction commit in the extended method will be completed successfully. We need more concrete guarantees of the fact that the record has been indeed created in the DB so we can execute additional logic that is dependent on presence of the new record there.

Please let us know if there is a solution for @AfterCreate listeners in Prisma API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain/client Issue in the "Client" domain: Prisma Client, Prisma Studio etc. kind/feature A request for a new feature. topic: clientExtensions topic: `query` Client Extension
Projects
None yet
Development

No branches or pull requests

5 participants
@janpio @vgarmash @jkomyno @MichaelNZ85 and others