{"_id":"prisma-extension-nested-operations","_rev":"1-55711d56376a4ac59a4a92fd6f316879","name":"prisma-extension-nested-operations","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"prisma-extension-nested-operations","version":"1.0.0","description":"Utils for creating Prisma client extensions with nested operations","main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm/index.js","scripts":{"build":"npm-run-all build:cjs build:esm","build:cjs":"tsc -p tsconfig.build.json","build:esm":"tsc -p tsconfig.esm.json","test:unit":"prisma generate && jest --config jest.config.unit.js","test:e2e":"./test/scripts/run-with-postgres.sh jest --config jest.config.e2e.js --runInBand","test":"./test/scripts/run-with-postgres.sh jest --runInBand","lint":"eslint ./src  --fix --ext .ts","typecheck":"npm run build:cjs -- --noEmit && npm run build:esm -- --noEmit","validate":"prisma generate && kcd-scripts validate lint,typecheck,test","semantic-release":"semantic-release","doctoc":"doctoc ."},"keywords":["prisma","client","extensions","extension"],"author":{"name":"Olivier Wilkinson"},"license":"Apache-2.0","dependencies":{"@open-draft/deferred-promise":"^2.1.0","lodash":"^4.17.21"},"peerDependencies":{"@prisma/client":"*"},"devDependencies":{"@prisma/client":"^5.0.0","@types/faker":"^5.5.9","@types/jest":"^29.2.5","@types/lodash":"^4.14.185","@typescript-eslint/eslint-plugin":"^4.14.0","@typescript-eslint/parser":"^4.14.0","doctoc":"^2.2.0","dotenv":"^16.0.3","eslint":"^7.6.0","faker":"^5.0.0","jest":"^29.3.1","kcd-scripts":"^5.0.0","npm-run-all":"^4.1.5","prisma":"^5.0.0","semantic-release":"^17.0.2","ts-jest":"^29.0.3","ts-node":"^9.1.1","typescript":"^4.1.3"},"repository":{"type":"git","url":"git+https://github.com/olivierwilkinson/prisma-extension-nested-operations.git"},"release":{"branches":["main","next"]},"publishConfig":{"access":"public"},"gitHead":"30e8437551d7c028065fe26855da6e2250bd7a70","bugs":{"url":"https://github.com/olivierwilkinson/prisma-extension-nested-operations/issues"},"homepage":"https://github.com/olivierwilkinson/prisma-extension-nested-operations#readme","_id":"prisma-extension-nested-operations@1.0.0","_nodeVersion":"16.20.2","_npmVersion":"7.24.2","dist":{"integrity":"sha512-FgpyOSYmHWnTxRD/bsY0UFgf5Fc5qe3y/GawIvr343kLdsyPLmzT2Uodnrcow+DhjmqiqlWFhwmQn1i6y0xH4A==","shasum":"81b3736df92c836290ed4117af968a305ddb1909","tarball":"https://registry.npmjs.org/prisma-extension-nested-operations/-/prisma-extension-nested-operations-1.0.0.tgz","fileCount":47,"unpackedSize":148392,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAJfRMTPtaWu+MZuXbWGvWcqew+IPxSjSujhZ/qBTN8cAiBgYjiLBAwaOxa2gTRstf1FeCN2seAWMXeVqBRbHHCGHg=="}]},"_npmUser":{"name":"olivierwilkinson","email":"olivier.wilkinson@gmail.com"},"directories":{},"maintainers":[{"name":"olivierwilkinson","email":"olivier.wilkinson@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/prisma-extension-nested-operations_1.0.0_1696539447172_0.8487255787856354"},"_hasShrinkwrap":false},"1.0.1":{"name":"prisma-extension-nested-operations","version":"1.0.1","description":"Utils for creating Prisma client extensions with nested operations","main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm/index.js","scripts":{"build":"npm-run-all build:cjs build:esm","build:cjs":"tsc -p tsconfig.build.json","build:esm":"tsc -p tsconfig.esm.json","test:unit":"prisma generate && jest --config jest.config.unit.js","test:e2e":"./test/scripts/run-with-postgres.sh jest --config jest.config.e2e.js --runInBand","test":"./test/scripts/run-with-postgres.sh jest --runInBand","lint":"eslint ./src  --fix --ext .ts","typecheck":"npm run build:cjs -- --noEmit && npm run build:esm -- --noEmit","validate":"prisma generate && kcd-scripts validate lint,typecheck,test","semantic-release":"semantic-release","doctoc":"doctoc ."},"keywords":["prisma","client","extensions","extension"],"author":{"name":"Olivier Wilkinson"},"license":"Apache-2.0","dependencies":{"@open-draft/deferred-promise":"^2.1.0","lodash":"^4.17.21"},"peerDependencies":{"@prisma/client":"*"},"devDependencies":{"@prisma/client":"^5.0.0","@types/faker":"^5.5.9","@types/jest":"^29.2.5","@types/lodash":"^4.14.185","@typescript-eslint/eslint-plugin":"^4.14.0","@typescript-eslint/parser":"^4.14.0","doctoc":"^2.2.0","dotenv":"^16.0.3","eslint":"^7.6.0","faker":"^5.0.0","jest":"^29.3.1","kcd-scripts":"^5.0.0","npm-run-all":"^4.1.5","prisma":"^5.0.0","semantic-release":"^17.0.2","ts-jest":"^29.0.3","ts-node":"^9.1.1","typescript":"^4.1.3"},"repository":{"type":"git","url":"git+https://github.com/olivierwilkinson/prisma-extension-nested-operations.git"},"release":{"branches":["main","next"]},"publishConfig":{"access":"public"},"gitHead":"70e1ef9558bd1e9aca8db5d7d386702c6b3cb127","bugs":{"url":"https://github.com/olivierwilkinson/prisma-extension-nested-operations/issues"},"homepage":"https://github.com/olivierwilkinson/prisma-extension-nested-operations#readme","_id":"prisma-extension-nested-operations@1.0.1","_nodeVersion":"16.20.2","_npmVersion":"7.24.2","dist":{"integrity":"sha512-mjMfVUplwCkrdClTqRP4793dOdPfT/H0zvV2uQ2R1BG2ZAqJ4LUEf//7f9LJdV5GhSOhp72w6b7DKJg1xIkgVA==","shasum":"d7e3febfc7f7470d6a9c358d9ae7e78098afd17b","tarball":"https://registry.npmjs.org/prisma-extension-nested-operations/-/prisma-extension-nested-operations-1.0.1.tgz","fileCount":47,"unpackedSize":147663,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDby3uTnndv8zG2AqDnJcD3DnFG9L50IFOU0s1SCNMU/gIgddfm1zw9Ms1ojlDUX4FAjH4HsU6MosUBqMwfs2uSZek="}]},"_npmUser":{"name":"olivierwilkinson","email":"olivier.wilkinson@gmail.com"},"directories":{},"maintainers":[{"name":"olivierwilkinson","email":"olivier.wilkinson@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/prisma-extension-nested-operations_1.0.1_1698931709688_0.884325566606172"},"_hasShrinkwrap":false}},"time":{"created":"2023-10-05T20:57:27.042Z","1.0.0":"2023-10-05T20:57:27.363Z","modified":"2023-11-02T13:28:30.012Z","1.0.1":"2023-11-02T13:28:29.831Z"},"maintainers":[{"name":"olivierwilkinson","email":"olivier.wilkinson@gmail.com"}],"description":"Utils for creating Prisma client extensions with nested operations","homepage":"https://github.com/olivierwilkinson/prisma-extension-nested-operations#readme","keywords":["prisma","client","extensions","extension"],"repository":{"type":"git","url":"git+https://github.com/olivierwilkinson/prisma-extension-nested-operations.git"},"author":{"name":"Olivier Wilkinson"},"bugs":{"url":"https://github.com/olivierwilkinson/prisma-extension-nested-operations/issues"},"license":"Apache-2.0","readme":"<div align=\"center\">\n<h1>Prisma Extension Nested Operations</h1>\n\n<p>Prisma Extension library that allows modifying operations on nested relations in a Prisma query.</p>\n\n<p>\n  Vanilla Prisma extensions are great for modifying top-level queries but\n  are still difficult to use when they must handle\n  <a href=\"https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#nested-writes\">nested writes</a>, <code>include</code>s, <code>select</code>s,\n  or modify <code>where</code> objects that reference relations.\n  This is talked about in greater depth in this <a href=\"https://github.com/prisma/prisma/issues/4211\">issue regarding nested middleware</a>, and the\n  same issue applies to extensions.\n</p>\n\n<p>\n  This library exports a <code>withNestedOperations()</code> helper that splits an <code>$allOperations()</code> hook into <code>$rootOperation()</code> and\n  <code>$allNestedOperations()</code> hooks.\n</p>\n\n</div>\n\n<hr />\n\n[![Build Status][build-badge]][build]\n[![version][version-badge]][package]\n[![MIT License][license-badge]][license]\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n[![PRs Welcome][prs-badge]][prs]\n\n## Table of Contents\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [`$rootOperation()`](#rootoperation)\n  - [`$allNestedOperations()` Params](#allnestedoperations-params)\n  - [Nested Writes](#nested-writes)\n    - [Changing Nested Write Operations](#changing-nested-write-operations)\n    - [Write Results](#write-results)\n  - [Where](#where)\n    - [Where Results](#where-results)\n  - [Include](#include)\n    - [Include Results](#include-results)\n  - [Select](#select)\n    - [Select Results](#select-results)\n  - [Relations](#relations)\n  - [Modifying Nested Write Params](#modifying-nested-write-params)\n  - [Modifying Where Params](#modifying-where-params)\n  - [Modifying Results](#modifying-results)\n  - [Errors](#errors)\n- [LICENSE](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\nThis module is distributed via [npm][npm] which is bundled with [node][node] and\nshould be installed as one of your project's dependencies:\n\n```\nnpm install prisma-extension-nested-operations\n```\n\n`@prisma/client` is a peer dependency of this library, so you will need to\ninstall it if you haven't already:\n\n```\nnpm install @prisma/client\n```\n\nYou must have at least @prisma/client version 4.16.0 installed.\n\n## Usage\n\nThe `withNestedOperations()` function takes and object with two properties, `$rootOperation()` and `$allNestedOperations()`.\nThe return value is an `$allOperations` hook, so it can be passed directly to an extensions `$allOperations` hook.\n\n```javascript\nimport { withNestedOperations } from \"prisma-extension-nested-operations\";\n\nclient.$extends({\n  query: {\n    $allModels: {\n      $allOperations: withNestedOperations({\n        async $rootOperation(params) {\n          // update root params here\n          const result = params.query(params.args);\n          // update root result here\n          return result;\n        },\n        async $allNestedOperations(params) {\n          // update nested params here\n          const result = await params.query(params.args);\n          // update nested result here\n          return result;\n        },\n      }),\n    },\n  },\n});\n```\n\n### `$rootOperation()`\n\nThe `$rootOperation()` hook is called with the same params as the `$allOperations()` hook, however the `params.args` object\nhas been updated by the args passed to the `$allNestedOperations()` query functions. The same pattern applies to the\nreturned result, it is the result of the query updated by the returned results from the `$allNestedOperations()` calls.\n\n### `$allNestedOperations()` Params\n\nThe params object passed to the `$allNestedOperations()` function is similar to the params passed to `$allOperations()`.\nIt has `args`, `model`, `operation`, and `query` fields, however there are some key differences:\n\n- the `operation` field adds the following options: 'connectOrCreate', 'connect', 'disconnect', 'include', 'select' and 'where'\n- the `query` field takes a second argument, which is the `operation` being performed. This is useful where the type of the nested operation should be changed.\n- there is an additional `scope` field that contains information specific to nested relations:\n\n  - the `parentParams` field contains the params object of the parent relation\n  - the `modifier` field contains any modifiers the params were wrapped in, for example `some` or `every`.\n  - the `logicalOperators` field contains any logical operators between the current relation and it's parent, for example `AND` or `NOT`.\n  - the `relations` field contains an object with the relation `to` the current model and `from` the model back to it's parent.\n\nFor more information on the `modifier` and `logicalOperators` fields see the [Where](#Where) section.\n\nFor more information on the `relations` field see the [Relations](#Relations) section.\n\nThe type for the params object is:\n\n```typescript\ntype NestedParams<ExtArgs> = {\n  query: (args: any, operation?: NestedOperation) => Prisma.PrismaPromise<any>;\n  model: keyof Prisma.TypeMap<ExtArgs>[\"model\"];\n  args: any;\n  operation: NestedOperation;\n  scope?: Scope<ExtArgs>;\n};\n\nexport type Scope<ExtArgs> = {\n  parentParams: Omit<NestedParams<ExtArgs>, \"query\">;\n  relations: { to: Prisma.DMMF.Field; from: Prisma.DMMF.Field };\n  modifier?: Modifier;\n  logicalOperators?: LogicalOperator[];\n};\n\ntype Modifier = \"is\" | \"isNot\" | \"some\" | \"none\" | \"every\";\n\ntype LogicalOperator = \"AND\" | \"OR\" | \"NOT\";\n\ntype Operation =\n  | \"create\"\n  | \"createMany\"\n  | \"update\"\n  | \"updateMany\"\n  | \"upsert\"\n  | \"delete\"\n  | \"deleteMany\"\n  | \"where\"\n  | \"include\"\n  | \"select\"\n  | \"connect\"\n  | \"connectOrCreate\"\n  | \"disconnect\";\n```\n\n### Nested Writes\n\nThe `$allNestedOperations()` function is called for every [nested write](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#nested-writes)\noperation in the query. The `operation` field is set to the operation being performed, for example \"create\" or \"update\".\nThe `model` field is set to the model being operated on, for example \"User\" or \"Post\".\n\nFor example take the following query:\n\n```javascript\nconst result = await client.user.update({\n  data: {\n    posts: {\n      update: {\n        where: { id: 1 },\n        data: { title: \"Hello World\" },\n      },\n    },\n  },\n});\n```\n\nThe `$allNestedOperations()` function will be called with:\n\n```javascript\n{\n  operation: 'update',\n  model: 'Post',\n  args: {\n    where: { id: 1 },\n    data: { title: 'Hello World' }\n  },\n  relations: {\n    to: { kind: 'object', name: 'posts', isList: true, ... },\n    from: { kind: 'object', name: 'author', isList: false, ... },\n  },\n  scope: [root params],\n}\n```\n\nSome nested writes can be passed as an array of operations. In this case the `$allNestedOperations()` function is called for each\noperation in the array. For example take the following query:\n\n```javascript\nconst result = await client.user.update({\n  data: {\n    posts: {\n      update: [\n        { where: { id: 1 }, data: { title: \"Hello World\" } },\n        { where: { id: 2 }, data: { title: \"Hello World 2\" } },\n      ],\n    },\n  },\n});\n```\n\nThe `$allNestedOperations()` function will be called with:\n\n```javascript\n {\n  operation: 'update',\n  model: 'Post',\n  args: {\n    where: { id: 1 },\n    data: { title: 'Hello World' }\n  },\n  relations: {\n    to: { kind: 'object', name: 'posts', isList: true, ... },\n    from: { kind: 'object', name: 'author', isList: false, ... },\n  },\n  scope: [root params],\n}\n```\n\nand\n\n```javascript\n {\n  operation: 'update',\n  model: 'Post',\n  args: {\n    where: { id: 2 },\n    data: { title: 'Hello World 2' }\n  },\n  relations: {\n    to: { kind: 'object', name: 'posts', isList: true, ... },\n    from: { kind: 'object', name: 'author', isList: false, ... },\n  },\n  scope: [root params],\n}\n```\n\n#### Changing Nested Write Operations\n\nThe `$allNestedOperations()` function can change the operation that is performed on the model. For example take the following query:\n\n```javascript\nconst result = await client.user.update({\n  data: {\n    posts: {\n      update: {\n        where: { id: 1 }\n        data: { title: 'Hello World' }\n      },\n    },\n  },\n});\n```\n\nThe `$allNestedOperations()` function could be used to change the operation to `upsert`:\n\n```javascript\nconst client = _client.$extends({\n  query: {\n    $allModels: {\n      $allOperations: withNestedOperations({\n        $rootOperation: (params) => {\n          return params.query(params.args);\n        },\n        $allNestedOperations: (params) => {\n          if (params.model === \"Post\" && params.operation === \"update\") {\n            return params.query(\n              {\n                where: params.args.where,\n                create: params.args.data,\n                update: params.args.data,\n              },\n              \"upsert\"\n            );\n          }\n          return params.query(params);\n        },\n      }),\n    },\n  },\n});\n```\n\nThe final query would be modified by the above `$allNestedOperations()` to:\n\n```javascript\nconst result = await client.user.update({\n  data: {\n    posts: {\n      upsert: {\n        where: { id: 1 },\n        create: { title: \"Hello World\" },\n        update: { title: \"Hello World\" },\n      },\n    },\n  },\n});\n```\n\nWhen changing the operation it is possible for the operation to already exist. In this case the resulting operations are merged.\nFor example take the following query:\n\n```javascript\nconst result = await client.user.update({\n  data: {\n    posts: {\n      update: {\n        where: { id: 1 },\n        data: { title: \"Hello World\" },\n      },\n      upsert: {\n        where: { id: 2 },\n        create: { title: \"Hello World 2\" },\n        update: { title: \"Hello World 2\" },\n      },\n    },\n  },\n});\n```\n\nUsing the same `$allNestedOperations()` defined before the update operation would be changed to an upsert operation, however there is\nalready an upsert operation so the two operations are merged into a upsert operation array with the new operation added to\nthe end of the array. When the existing operation is already a list of operations the new operation is added to the end of\nthe list. The final query in this case would be:\n\n```javascript\nconst result = await client.user.update({\n  data: {\n    posts: {\n      upsert: [\n        {\n          where: { id: 2 },\n          create: { title: \"Hello World 2\" },\n          update: { title: \"Hello World 2\" },\n        },\n        {\n          where: { id: 1 },\n          create: { title: \"Hello World\" },\n          update: { title: \"Hello World\" },\n        },\n      ],\n    },\n  },\n});\n```\n\nSometimes it is not possible to merge the operations together in this way. The `createMany` operation does not support\noperation arrays so the `data` field of the `createMany` operation is merged instead. For example take the following query:\n\n```javascript\nconst result = await client.user.create({\n  data: {\n    posts: {\n      createMany: {\n        data: [{ title: \"Hello World\" }, { title: \"Hello World 2\" }],\n      },\n      create: {\n        title: \"Hello World 3\",\n      },\n    },\n  },\n});\n```\n\nIf the `create` operation was changed to be a `createMany` operation the `data` field would be added to the end of the existing\n`createMany` operation. The final query would be:\n\n```javascript\nconst result = await client.user.create({\n  data: {\n    posts: {\n      createMany: {\n        data: [\n          { title: \"Hello World\" },\n          { title: \"Hello World 2\" },\n          { title: \"Hello World 3\" },\n        ],\n      },\n    },\n  },\n});\n```\n\nIt is also not possible to merge the operations together by creating an array of operations for non-list relations. For\nexample take the following query:\n\n```javascript\nconst result = await client.user.update({\n  data: {\n    profile: {\n      create: {\n        bio: \"My personal bio\",\n        age: 30,\n      },\n      update: {\n        where: { id: 1 },\n        data: { bio: \"Updated bio\" },\n      },\n    },\n  },\n});\n```\n\nIf the `update` operation was changed to be a `create` operation using the following extension:\n\n```javascript\nconst client = _client.$extends({\n  query: {\n    $allModels: {\n      $allOperations: withNestedOperations({\n        $rootOperation: (params) => {\n          return params.query(params.args);\n        },\n        $allNestedOperations: (params) => {\n          if (params.model === \"Profile\" && params.operation === \"update\") {\n            return params.query(params.args.data, \"create\");\n          }\n          return params.query(params);\n        },\n      }),\n    },\n  },\n});\n```\n\nThe `create` operation from the `update` operation would need be merged with the existing `create` operation, however since\n`profile` is not a list relation we must merge together the resulting objects instead, resulting in the final query:\n\n```javascript\nconst result = await client.user.create({\n  data: {\n    profile: {\n      create: {\n        bio: \"Updated bio\",\n        age: 30,\n      },\n    },\n  },\n});\n```\n\n#### Write Results\n\nThe `query` function of `$allNestedOperations()` calls for nested write operations always return `undefined` as their result.\nThis is because the results returned from the root query may not include the data for a particular nested write.\n\nFor example take the following query:\n\n```javascript\nconst result = await client.user.update({\n  data: {\n    profile: {\n      create: {\n        bio: \"My personal bio\",\n        age: 30,\n      },\n    }\n    posts: {\n      updateMany: {\n        where: {\n          published: false,\n        },\n        data: {\n          published: true,\n        },\n      },\n    },\n  },\n  select: {\n    id: true,\n    posts: {\n      where: {\n        title: {\n          contains: \"Hello\",\n        },\n      },\n      select: {\n        id: true,\n      },\n    },\n  }\n});\n```\n\nThe `profile` field is not included in the `select` object so the result of the `create` operation will not be included in\nthe root result. The `posts` field is included in the `select` object but the `where` object only includes posts with\ntitles that contain \"Hello\" and returns only the \"id\" field, in this case it is not possible to match the result of the\n`updateMany` operation to the returned Posts.\n\nSee [Modifying Results](#modifying-results) for more information on how to update the results of queries.\n\n### Where\n\nThe `where` operation is called for any relations found inside where objects in params.\n\nNote that the `where` operation is not called for the root where object, this is because you need the root operation to know\nwhat properties the root where object accepts. For nested where objects this is not a problem as they always follow the\nsame pattern.\n\nTo see where the `where` operation is called take the following query:\n\n```javascript\nconst result = await client.user.findMany({\n  where: {\n    posts: {\n      some: {\n        published: true,\n      },\n    },\n  },\n});\n```\n\nThe where object above produces a call for \"posts\" relation found in the where object. The `modifier` field is set to\n\"some\" since the where object is within the \"some\" field.\n\n```javascript\n{\n  operation: 'where',\n  model: 'Post',\n  args: {\n    published: true,\n  },\n  scope: {\n    parentParams: {...}\n    modifier: 'some',\n    relations: {...}\n  },\n}\n```\n\nRelations found inside where AND, OR and NOT logical operators are also found and called with the `$allNestedOperations()` function,\nhowever the `where` operation is not called for the logical operators themselves. For example take the following query:\n\n```javascript\nconst result = await client.user.findMany({\n  where: {\n    posts: {\n      some: {\n        published: true,\n        AND: [\n          {\n            title: \"Hello World\",\n          },\n          {\n            comments: {\n              every: {\n                text: \"Great post!\",\n              },\n            },\n          },\n        ],\n      },\n    },\n  },\n});\n```\n\nThe `$allNestedOperations()` function will be called with the params for \"posts\" similarly to before, however it will also be called\nwith the following params:\n\n```javascript\n{\n  operation: 'where',\n  model: 'Comment',\n  args: {\n    text: \"Great post!\",\n  },\n  scope: {\n    parentParams: {...}\n    modifier: 'every',\n    logicalOperators: ['AND'],\n    relations: {...}\n  },\n}\n```\n\nSince the \"comments\" relation is found inside the \"AND\" logical operator the\n\\$allNestedOperations is called for it. The `modifier` field is set to \"every\" since the where object is in the \"every\" field and\nthe `logicalOperators` field is set to `['AND']` since the where object is inside the \"AND\" logical operator.\n\nNotice that the `$allNestedOperations()` function is not called for the first item in the \"AND\" array, this is because the first item\ndoes not contain any relations.\n\nThe `logicalOperators` field tracks all the logical operators between the `parentParams` and the current params. For\nexample take the following query:\n\n```javascript\nconst result = await client.user.findMany({\n  where: {\n    AND: [\n      {\n        NOT: {\n          OR: [\n            {\n              posts: {\n                some: {\n                  published: true,\n                },\n              },\n            },\n          ],\n        },\n      },\n    ],\n  },\n});\n```\n\nThe `$allNestedOperations()` function will be called with the following params:\n\n```javascript\n{\n  operation: 'where',\n  model: 'Post',\n  args: {\n    published: true,\n  },\n  scope: {\n    parentParams: {...}\n    modifier: 'some',\n    logicalOperators: ['AND', 'NOT', 'OR'],\n    relations: {...},\n  },\n}\n```\n\nThe `where` operation is also called for relations found in the `where` field of includes and selects. For example:\n\n```javascript\nconst result = await client.user.findMany({\n  select: {\n    posts: {\n      where: {\n        published: true,\n      },\n    },\n  },\n});\n```\n\nThe `$allNestedOperations()` function will be called with the following params:\n\n```javascript\n{\n  operation: 'where',\n  model: 'Post',\n  args: {\n    published: true,\n  },\n  scope: {...}\n}\n```\n\n#### Where Results\n\nThe `query` function for a `where` operation always resolves with `undefined`.\n\n### Include\n\nThe `include` operation will be called for any included relation. The `args` field will contain the object or boolean\npassed as the relation include. For example take the following query:\n\n```javascript\nconst result = await client.user.findMany({\n  include: {\n    profile: true,\n    posts: {\n      where: {\n        published: true,\n      },\n    },\n  },\n});\n```\n\nFor the \"profile\" relation the `$allNestedOperations()` function will be called with:\n\n```javascript\n{\n  operation: 'include',\n  model: 'Profile',\n  args: true,\n  scope: {...}\n}\n```\n\nand for the \"posts\" relation the `$allNestedOperations()` function will be called with:\n\n```javascript\n{\n  operation: 'include',\n  model: 'Post',\n  args: {\n    where: {\n      published: true,\n    },\n  },\n  scope: {...}\n}\n```\n\n#### Include Results\n\nThe `query` function for an `include` operation resolves with the result of the `include` operation. For example take the\nfollowing query:\n\n```javascript\nconst result = await client.user.findMany({\n  include: {\n    profile: true,\n  },\n});\n```\n\nThe `$allNestedOperations()` function for the \"profile\" relation will be called with:\n\n```javascript\n{\n  operation: 'include',\n  model: 'Profile',\n  args: true,\n  scope: {...}\n}\n```\n\nAnd the `query` function will resolve with the result of the `include` operation, in this case something like:\n\n```javascript\n{\n  id: 2,\n  bio: 'My personal bio',\n  age: 30,\n  userId: 1,\n}\n```\n\nFor relations that are included within a list of parent results the `query` function will resolve with a flattened array\nof all the models from each parent result. For example take the following query:\n\n```javascript\nconst result = await client.user.findMany({\n  include: {\n    posts: true,\n  },\n});\n```\n\nIf the root result looks like the following:\n\n```javascript\n[\n  {\n    id: 1,\n    name: \"Alice\",\n    posts: [\n      {\n        id: 1,\n        title: \"Hello World\",\n        published: false,\n        userId: 1,\n      },\n      {\n        id: 2,\n        title: \"My first published post\",\n        published: true,\n        userId: 1,\n      },\n    ],\n  },\n  {\n    id: 2,\n    name: \"Bob\",\n    posts: [\n      {\n        id: 3,\n        title: \"Clean Code\",\n        published: true,\n        userId: 2,\n      },\n    ],\n  },\n];\n```\n\nThe `query` function for the \"posts\" relation will resolve with the following:\n\n```javascript\n[\n  {\n    id: 1,\n    title: \"Hello World\",\n    published: false,\n    userId: 1,\n  },\n  {\n    id: 2,\n    title: \"My first published post\",\n    published: true,\n    userId: 1,\n  },\n  {\n    id: 3,\n    title: \"Clean Code\",\n    published: true,\n    userId: 2,\n  },\n];\n```\n\nFor more information on how to modify the results of an `include` operation see the [Modifying Results](#modifying-results)\n\n### Select\n\nSimilarly to the `include` operation, the `select` operation will be called for any selected relation with the `args` field\ncontaining the object or boolean passed as the relation select. For example take the following query:\n\n```javascript\nconst result = await client.user.findMany({\n  select: {\n    posts: true,\n    profile: {\n      select: {\n        bio: true,\n      },\n    },\n  },\n});\n```\n\nand for the \"posts\" relation the `$allNestedOperations()` function will be called with:\n\n```javascript\n{\n  operation: 'select',\n  model: 'Post',\n  args: true,\n  scope: {...}\n}\n```\n\nFor the \"profile\" relation the `$allNestedOperations()` function will be called with:\n\n```javascript\n{\n  operation: 'select',\n  model: 'Profile',\n  args: {\n    bio: true,\n  },\n  scope: {...}\n}\n```\n\n#### Select Results\n\nThe `query` function for a `select` operation resolves with the result of the `select` operation. This is the same as the\n`include` operation. See the [Include Results](#include-results) section for more information.\n\n### Relations\n\nThe `relations` field of the `scope` object contains the relations relevant to the current model. For example take the\nfollowing query:\n\n```javascript\nconst result = await client.user.create({\n  data: {\n    email: \"test@test.com\",\n    profile: {\n      create: {\n        bio: \"Hello World\",\n      },\n    },\n    posts: {\n      create: {\n        title: \"Hello World\",\n      },\n    },\n  },\n});\n```\n\nThe `$allNestedOperations()` function will be called with the following params for the \"profile\" relation:\n\n```javascript\n{\n  operation: 'create',\n  model: 'Profile',\n  args: {\n    bio: \"Hello World\",\n  },\n  scope: {\n    parentParams: {...}\n    relations: {\n      to: { name: 'profile', kind: 'object', isList: false, ... },\n      from: { name: 'user', kind: 'object', isList: false, ... },\n    },\n  },\n}\n```\n\nand the following params for the \"posts\" relation:\n\n```javascript\n{\n  operation: 'create',\n  model: 'Post',\n  args: {\n    title: \"Hello World\",\n  },\n  scope: {\n    parentParams: {...}\n    relations: {\n      to: { name: 'posts', kind: 'object', isList: true, ... },\n      from: { name: 'author', kind: 'object', isList: false, ... },\n    },\n  },\n}\n```\n\n### Modifying Nested Write Params\n\nWhen writing extensions that modify the params of a query you should first write the `$rootOperation()` hook as if it were\nan `$allOperations()` hook, and then add the `$allNestedOperations()` hook.\n\nSay you are writing middleware that sets a default value when creating a model for a particular model:\n\n```javascript\nconst client = _client.$extends({\n  query: {\n    $allModels: {\n      $allOperations: withNestedOperations({\n        async $rootOperation(params) {\n          // we only want to add default values for the \"Invite\" model\n          if (params.model !== \"Invite\") {\n            return params.query(params.args);\n          }\n\n          if (params.operation === \"create\" && !params.args.data.code) {\n            params.args.data.code = createCode();\n          }\n\n          if (params.operation === \"upsert\" && !params.args.create.code) {\n            params.args.create.code = createCode();\n          }\n\n          if (params.operation === \"createMany\") {\n            params.args.data.forEach((data) => {\n              if (!data.code) {\n                data.code = createCode();\n              }\n            });\n          }\n\n          return params.query(params.args);\n        },\n        async $allNestedOperations(params) {\n          return params.query(params.args);\n        },\n      }),\n    },\n  },\n});\n```\n\nThen add conditions for the different args and operations that can be found in nested writes:\n\n```javascript\nconst client = _client.$extends({\n  query: {\n    $allModels: {\n      $allOperations: withNestedOperations({\n        async $rootOperation(params) {\n          [...]\n        },\n        async $allNestedOperations(params) {\n          // we only want to add default values for the \"Invite\" model\n          if (params.model !== \"Invite\") {\n            return params.query(params.args);\n          }\n\n          // when the \"create\" operation is from a nested write the data is not in the \"data\" field\n          if (params.operation === \"create\" && !params.args.code) {\n            params.args.code = createCode();\n          }\n\n          // handle the \"connectOrCreate\" operation\n          if (params.operation === \"connectOrCreate\" && !params.args.create.code) {\n            params.args.create.code = createCode();\n          }\n\n          // pass args to query\n          return params.query(params.args);\n        },\n      }),\n    },\n  },\n});\n```\n\n### Modifying Where Params\n\nWhen writing extensions that modify the where params of a query you should first write the `$rootOperation()` hook as\nif it were an `$allOperations()` hook, this is because the `where` operation is not called for the root where object and so you\nwill need to handle it manually.\n\nSay you are writing an extension that excludes models with a particular field, let's call it \"invisible\" rather than\n\"deleted\" to make this less familiar:\n\n```javascript\nconst client = _client.$extends({\n  query: {\n    $allModels: {\n      $allOperations: withNestedOperations({\n        async $rootOperation(params) {\n          // don't handle operations that only accept unique fields such as findUnique or upsert\n          if (\n            params.operation === \"findFirst\" ||\n            params.operation === \"findFirstOrThrow\" ||\n            params.operation === \"findMany\" ||\n            params.operation === \"updateMany\" ||\n            params.operation === \"deleteMany\" ||\n            params.operation === \"count\" ||\n            params.operation === \"aggregate\"\n          ) {\n            return params.query({\n              ...params.args,\n              where: {\n                ...params.args.where,\n                invisible: false,\n              },\n            });\n          }\n\n          return params.query(params.args);\n        },\n        async $allNestedOperations(params) {\n          return params.query(params.args);\n        },\n      }),\n    },\n  },\n});\n```\n\nThen add conditions for the `where` operation:\n\n```javascript\nconst client = _client.$extends({\n  query: {\n    $allModels: {\n      $allOperations: withNestedOperations({\n        async $rootOperation(params) {\n          [...]\n        },\n        async $allNestedOperations(params) {\n          // handle the \"where\" operation\n          if (params.operation === \"where\") {\n            return params.query({\n              ...params.args,\n              invisible: false,\n            });\n          }\n\n          return params.query(params.args);\n        },\n      }),\n    },\n  },\n});\n```\n\n### Modifying Results\n\nWhen writing extensions that modify the results of a query you should take the following process:\n\n- handle all the root cases in the `$rootOperation()` hook the same way you would with a `$allOperations()` hook.\n- handle nested results using the `include` and `select` operations in the `$allNestedOperations()` hook.\n\nSay you are writing middleware that adds a timestamp to the results of a query. You would first handle the root cases:\n\n```javascript\nconst client = _client.$extends({\n  query: {\n    $allModels: {\n      $allOperations: withNestedOperations({\n        async $rootOperation(params) {\n          const result = await params.query(params.args);\n\n          // ensure result is defined\n          if (!result) return result;\n\n          // handle root operations\n          if (\n            params.operation === \"findFirst\" ||\n            params.operation === \"findFirstOrThrow\" ||\n            params.operation === \"findUnique\" ||\n            params.operation === \"findUniqueOrThrow\" ||\n            params.operation === \"create\" ||\n            params.operation === \"update\" ||\n            params.operation === \"upsert\" ||\n            params.operation === \"delete\"\n          ) {\n            result.timestamp = Date.now();\n            return result;\n          }\n\n          if (params.operation === \"findMany\") {\n            const result = await params.query(params.args);\n            result.forEach((model) => {\n              model.timestamp = Date.now();\n            });\n            return result;\n          }\n\n          return result;\n        },\n        async $allNestedOperations(params) {\n          return params.query(params.args);\n        },\n      }),\n    },\n  },\n});\n```\n\nThen you would handle the nested results using the `include` and `select` operations:\n\n```javascript\nconst client = _client.$extends({\n  query: {\n    $allModels: {\n      $allOperations: withNestedOperations({\n        async $rootOperation(params) {\n          [...]\n        },\n        async $allNestedOperations(params) {\n          const result = await next(params);\n\n          // ensure result is defined\n          if (!result) return result;\n\n          // handle nested operations\n          if (params.operation === \"include\" || params.operation === \"select\") {\n            if (Array.isArray(result)) {\n              result.forEach((model) => {\n                model.timestamp = Date.now();\n              });\n            } else {\n              result.timestamp = Date.now();\n            }\n            return result;\n          }\n\n          return result;\n        },\n      }),\n    },\n  },\n});\n```\n\nYou could also write the above middleware by creating new objects for each result rather than mutating the existing\nobjects:\n\n```javascript\nconst client = _client.$extends({\n  query: {\n    $allModels: {\n      $allOperations: withNestedOperations({\n        async $rootOperation(params) {\n          [...]\n        },\n        async $allNestedOperations(params) {\n          const result = await next(params);\n\n          // ensure result is defined\n          if (!result) return result;\n\n          // handle nested operations\n          if (params.operation === \"include\" || params.operation === \"select\") {\n            if (Array.isArray(result)) {\n              return result.map((model) => ({\n                ...model,\n                timestamp: Date.now(),\n              });\n            }\n\n            return {\n              ...result,\n              timestamp: Date.now(),\n            };\n          }\n\n          return result;\n        },\n      }),\n    },\n  },\n});\n```\n\nNOTE: When modifying results from `include` or `select` operations it is important to either mutate the existing objects or\nspread the existing objects into the new objects. This is because `createNestedMiddleware` needs some fields from the\noriginal objects in order to correct update the root results.\n\n### Errors\n\nIf any middleware throws an error at any point then the root query will throw with that error. Any middleware that is\npending will have it's promises rejects at that point.\n\n## LICENSE\n\nApache 2.0\n\n[npm]: https://www.npmjs.com/\n[node]: https://nodejs.org\n[build-badge]: https://github.com/olivierwilkinson/prisma-extension-nested-operations/workflows/prisma-extension-nested-operations/badge.svg\n[build]: https://github.com/olivierwilkinson/prisma-extension-nested-operations/actions?query=branch%3Amain+workflow%3Aprisma-extension-nested-operations\n[version-badge]: https://img.shields.io/npm/v/prisma-extension-nested-operations.svg?style=flat-square\n[package]: https://www.npmjs.com/package/prisma-extension-nested-operations\n[downloads-badge]: https://img.shields.io/npm/dm/prisma-extension-nested-operations.svg?style=flat-square\n[npmtrends]: http://www.npmtrends.com/prisma-extension-nested-operations\n[license-badge]: https://img.shields.io/npm/l/prisma-extension-nested-operations.svg?style=flat-square\n[license]: https://github.com/olivierwilkinson/prisma-extension-nested-operations/blob/master/LICENSE\n[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\n[prs]: http://makeapullrequest.com\n[coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square\n[coc]: https://github.com/olivierwilkinson/prisma-extension-nested-operations/blob/master/other/CODE_OF_CONDUCT.md\n","readmeFilename":"README.md"}