prisma / prisma-client-js

Type-safe database client for TypeScript & Node.js (ORM replacement)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support afterCreate/afterUpdate database triggers

CDDelta opened this issue · comments

Problem

The Prisma client offers a way to modify inbound queries and updates to the database through middleware but offers no way to trigger actions based on the full result of a create/update operation.

For example, say I want to create a Post model with a default id that's generated by the database and a corresponding PostRevsion model for the creation and every update to the Post. There's no way for one to use middleware to create a PostRevision and reference back to the Post because the middleware has no access to the final database generated data. It is a similar case with updates which might only partially update the contents of Post meaning the middleware has no access to the complete Post to create a revision of.

Suggested solution

Copied from #653:
Prisma Client should have a way to define functions before and after crud operations. Here is an example that I thought of which would be pretty common. The Sequelize link I posted above also has plenty of examples and concepts, basically an instruction guide on what to include.

prisma = new PrismaClient();
prisma.setTriggers(prisma => ({
  beforeOrderCreate: async (args: OrderCreateArgs): Promise<OrderCreateArgs> => {
  const grandTotal = calcGrandTotal(args.data.lineItems.create);
  // Error can be thrown here
  await processPayment(grandTotal, args.data.user.connect.id);
  return {
    ...args, 
    { 
      data: {
        ...args.data, 
        grandTotal 
      }
    }
  })
  afterOrderCreate: async (order: Order): Promise<void> => {
    const user = await prisma.order.findOne({ where: { id: order.id } }).user();
    if (user.acceptsEmail) {
       await sendEmailToCustomer(order.id, order.grandTotal, user.name);
    }
  },
  afterOrderUpdate: async (newOrder: Order, oldOrder: Order): Promise<void> => {
    if (newOrder.grandTotal !== oldOrder.grandTotal) {
      await prisma.creditNote.create({
        data: {
          amount: newOrder.grandTotal - oldOrder.grandTotal,
          order: { connect: { id: newOrder.id }
        }
      }
    }
  }
}))

From me:
This feature would be even more powerful if coupled with long-running transactions but it's perhaps already too complex.

Alternatives

Alternatively, providing a way to define these triggers within schema.prisma would be sufficient for my use case while also providing the transactional guarantees required.

Additional context

Ok, I've had a look at Prisma Migrate and the migration workflow and generated SQL it provides seems to work for the alternative I outlined. I'll close this issue once I've explored it further. :)

I've managed to workaround this issue by creating database triggers using SQL. Closing.