Util for calling Prisma middleware for nested write operations.
Existing Prisma middleware is called once for every operation, but due to some operations containing nested writes it can become complex to ensure middleware is applied in all scenarios. See the existing issue regarding nested middleware for more information.
This module is distributed via npm which is bundled with node and should be installed as one of your project's dependencies:
npm install --save prisma-nested-middleware
Pass a middleware function to createNestedMiddleware, the returned middleware can be passed to Prisma client's $use method:
import { createNestedMiddleware } from 'prisma-nested-middleware'
client.$use(createNestedMiddleware(async (params, next) => {
// update params here
const result = await next(params)
// update result here
return result;
));The middleware function passed to createNestedMiddleware is called for every
nested write operation.
There are some differences to note when using nested middleware:
- the list of actions that might be in params is expanded to include
connectOrCreate,includeandselect. - The parent operation's params have been added to the params of nested middleware as a
scopeobject. This is useful when the parent is relevant, for example when handling aconnectOrCreateand you need to know the parent being connected to. - when handling a nested
createactionparams.argsdoes not include adatafield, that must be handled manually. You can use the existence ofparams.scopeto know when to handle a nestedcreate. - the return value of
nextmatches the part of the response that the middleware was called for. For example if the middleware function is called for a nested create, thenextfunction resolves with the value of that create. - if a relation is not included using
includethen that middleware'snextfunction will resolve withundefined. - if a nested operation's result is within an array then the nested operation's
nextfunction returns a flattened array of all the models found in the parent array. See Operations Nested in Lists for more information.
It is helpful to walk through the lifecycle of an operation:
For the following update
client.user.update({
where: { id: 1 },
data: {
comments: {
update: {
where: { id: 2 },
data: {
post: {
connectOrCreate: {
where: { id: 3 },
create: {
title: 'Hello World',
},
},
},
},
},
},
},
include: {
comments: {
include: {
post: {
select: {
title: true,
},
},
},
},
},
});createNestedMiddleware calls the passed middleware function from the most deeply nested operation up to the top level operation. For the above example the middleware function is called in the following order:
{ model: 'Post', action: 'connectOrCreate', args: { create: {...}, connect: {...} } }{ model: 'Post', action: 'select', args: { title: true } }{ model: 'Post', action: 'include', args: { select: { title: true } } }{ model: 'Comment', action: 'update', args: { where: { id: 2 }, data: {...} } }{ model: 'Comment', action: 'include', args: { include: { post: { select: { title: true } } } } }{ model: 'User', action: 'update', args: { where: { id: 1 }, data: {...} } }
The params object passed to the next function will modify the relevant part of the original params object. Once all the middleware calls have called next the updated params object is passed to the Prisma client.
The result of the Prisma client operation is then returned from the next functions according to the part of the result they are concerned with. So the next function called for the User model resolves with the User model's result, the next function called for the Comment model resolves with the Comment model's result, and so on.
Modifications to the result are made in the same way as modifications to the params. Once all the middleware calls have returned their slice of the result the combined result object is returned from the Prisma client method, in this case client.user.update.
If any middleware throws an error at any point then client.country.update will throw with that error.
When a next function needs to return a relation that is nested within a list it combines all the relation values into a single flat array. This means middleware only has to handle flat arrays of results which makes modifying the result before it is returned easier. If the result's parent is needed then it is possible to go through the parent middleware and traverse that relation; only a single depth of relation needs to be traversed as the middleware will be called for each layer.
For example if a comment is created within an array of posts, the next function for comments returns a flattened array of all the comments found within the posts array. When the flattened array is returned at the end of the middleware function the comments are put back into their corresponding posts.
Apache 2.0