Have you ever accidentally delete all the table data with Prisma?

#prisma  

#middleware  

#updatemany  

#deletemany  

Tue Aug 02 2022

This is not a story about a bug but rather a feature!

Have you ever used table.updateMany or table.deleteMany in your code? we have and it was something like this:

const deleteChildrenOfThatParent = await prisma.children.deleteMany({
  where: {
    parentEmail: {
      equal: parent.email,
    },
  },
})

It was working great for a long time and you know what, we had TS checking the types too but somehow someday we lost all of our children data in the table. We checked the database (Postgres) and we found out that at a certain time, all of the data has been wiped out! We blamed DevOps, hackers, AWS, ... but it was us not anyone else.

Finally, we figured out that we might have a situation where parent.email was undefined. But still, we couldn't believe that deleteMany with a condition executed that. After some testing, we were shocked but that was true and actually, it made sense... our "where" clause made the code in a way that we deleted all the children only because of that undefined. we basically ran:

const deleteChildrenOfThatParent = await prisma.children.deleteMany({
  where: {},
  },
})

What could have saved us though?

Retro

So we had a retro around it and there were some angry stakeholders there! We promised them that this would never happen again! first thing first we just found every updateMany and deleteMany function and we bookmarked them. We could just put a condition before each one to check for undefined but this wasn't a permanent solution or scalable one. After some investigation, we found a great solution, yeah Prisma itself came to help :p

Prisma Middleware

So what we needed to implement was something in the lower layer on the Prisma which could check all the mutations to make sure we're not removing data that we don't want to delete. Prisma Middleware is that thing YES!

we just implemented the middleware in a way that we check every query (mutations) and filter out the updateMany and deleteMany ones and make sure that there is a condition on them!

async function main() {
  prisma.$use(async (params, next) => {
    // Check incoming query type
    if (params.model === 'Post') {
      if (params.action === 'deleteMany' || params.action === 'updateMany') {
        //Check the Where clause in params.arg.where, if it's empty just return
      }
    }
    return next(params)
  })

© Copyright 2022 Farmin Farzin