This 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 had a situation that 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
functions and we bookmarked them. We could just put a condition before each one to check for undefined but this wasn't a permanent solution neither 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)
})
Top comments (0)