diff --git a/content/200-orm/200-prisma-client/100-queries/100-query-optimization-performance.mdx b/content/200-orm/200-prisma-client/100-queries/100-query-optimization-performance.mdx index 06593ad5e8..e4354e9313 100644 --- a/content/200-orm/200-prisma-client/100-queries/100-query-optimization-performance.mdx +++ b/content/200-orm/200-prisma-client/100-queries/100-query-optimization-performance.mdx @@ -42,8 +42,6 @@ To get started, follow the [integration guide](/optimize/getting-started) and ad You can also [log query events at the client level](/orm/prisma-client/observability-and-logging/logging#event-based-logging) to view the generated queries, their parameters, and execution times. -If you are particularly focused on monitoring query duration, consider using [logging middleware](/orm/prisma-client/client-extensions/middleware/logging-middleware). - ::: ## Using bulk queries diff --git a/content/200-orm/200-prisma-client/300-client-extensions/120-query.mdx b/content/200-orm/200-prisma-client/300-client-extensions/120-query.mdx index 733c03041b..9c408e7620 100644 --- a/content/200-orm/200-prisma-client/300-client-extensions/120-query.mdx +++ b/content/200-orm/200-prisma-client/300-client-extensions/120-query.mdx @@ -15,7 +15,7 @@ Prisma Client extensions are Generally Available from versions 4.16.0 and later. You can use the `query` [Prisma Client extensions](/orm/prisma-client/client-extensions) component type to hook into the query life-cycle and modify an incoming query or its result. -You can use Prisma Client extensions `query` component to create independent clients. This provides an alternative to [middlewares](/orm/prisma-client/client-extensions/middleware). You can bind one client to a specific filter or user, and another client to another filter or user. For example, you might do this to get [user isolation](/orm/prisma-client/client-extensions#extended-clients) in a row-level security (RLS) extension. In addition, unlike middlewares the `query` extension component gives you end-to-end type safety. [Learn more about `query` extensions versus middlewares](#query-extensions-versus-middlewares). +You can use Prisma Client extensions `query` component to create independent clients with customized behavior. You can bind one client to a specific filter or user, and another client to another filter or user. For example, you might do this to get [user isolation](/orm/prisma-client/client-extensions#extended-clients) in a row-level security (RLS) extension. The `query` extension component provides end-to-end type safety for all your custom queries. @@ -283,20 +283,3 @@ const transactionExtension = Prisma.defineExtension((prisma) => const prisma = new PrismaClient().$extends(transactionExtension) ``` -## Query extensions versus middlewares - -You can use query extensions or [middlewares](/orm/prisma-client/client-extensions/middleware) to hook into the query life-cycle and modify an incoming query or its result. Client extensions and middlewares differ in the following ways: - -- Middlewares always apply globally to the same client. Client extensions are isolated, unless you deliberately combine them. [Learn more about client extensions](/orm/prisma-client/client-extensions#about-prisma-client-extensions). - - For example, in a row-level security (RLS) scenario, you can keep each user in an entirely separate client. With middlewares, all users are active in the same client. -- During application execution, with extensions you can choose from one or more extended clients, or the standard Prisma Client. With middlewares, you cannot choose which client to use, because there is only one global client. -- Extensions benefit from end-to-end type safety and inference, but middlewares don't. - -You can use Prisma Client extensions in all scenarios where middlewares can be used. - -### If you use the `query` extension component and middlewares - -If you use the `query` extension component and middlewares in your project, then the following rules and priorities apply: - -- In your application code, you must declare all your middlewares on the main Prisma Client instance. You cannot declare them on an extended client. -- In situations where middlewares and extensions with a `query` component execute, Prisma Client executes the middlewares before it executes the extensions with the `query` component. Prisma Client executes the individual middlewares and extensions in the order in which you instantiated them with `$use` or `$extends`. diff --git a/content/200-orm/200-prisma-client/300-client-extensions/500-middleware/100-soft-delete-middleware.mdx b/content/200-orm/200-prisma-client/300-client-extensions/500-middleware/100-soft-delete-middleware.mdx deleted file mode 100644 index cdfe0ac784..0000000000 --- a/content/200-orm/200-prisma-client/300-client-extensions/500-middleware/100-soft-delete-middleware.mdx +++ /dev/null @@ -1,681 +0,0 @@ ---- -title: 'Middleware sample: soft delete' -metaTitle: 'Middleware sample: soft delete (Reference)' -metaDescription: 'How to use middleware to intercept deletes and set a field value instead of deleting the record.' -toc_max_heading_level: 4 ---- - - - -The following sample uses [middleware](/orm/prisma-client/client-extensions/middleware) to perform a **soft delete**. Soft delete means that a record is **marked as deleted** by changing a field like `deleted` to `true` rather than actually being removed from the database. Reasons to use a soft delete include: - -- Regulatory requirements that mean you have to keep data for a certain amount of time -- 'Trash' / 'bin' functionality that allows users to restore content that was deleted - - - -**Note:** This page demonstrates a sample use of middleware. We do not intend the sample to be a fully functional soft delete feature and it does not cover all edge cases. For example, the middleware does not work with nested writes and therefore won't capture situations where you use `delete` or `deleteMany` as an option e.g. in an `update` query. - - - -This sample uses the following schema - note the `deleted` field on the `Post` model: - -```prisma highlight=28;normal -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - -generator client { - provider = "prisma-client-js" -} - -model User { - id Int @id @default(autoincrement()) - name String? - email String @unique - posts Post[] - followers User[] @relation("UserToUser") - user User? @relation("UserToUser", fields: [userId], references: [id]) - userId Int? -} - -model Post { - id Int @id @default(autoincrement()) - title String - content String? - user User? @relation(fields: [userId], references: [id]) - userId Int? - tags Tag[] - views Int @default(0) - //highlight-next-line - deleted Boolean @default(false) -} - -model Category { - id Int @id @default(autoincrement()) - parentCategory Category? @relation("CategoryToCategory", fields: [categoryId], references: [id]) - category Category[] @relation("CategoryToCategory") - categoryId Int? -} - -model Tag { - tagName String @id // Must be unique - posts Post[] -} -``` - - - -
-Questions answered in this page - -- How to implement soft delete middleware? -- How to block reads/updates of deleted records? -- What are trade-offs of middleware-based soft delete? - -
- -## Step 1: Store status of record - -Add a field named `deleted` to the `Post` model. You can choose between two field types depending on your requirements: - -- `Boolean` with a default value of `false`: - - ```prisma highlight=4;normal - model Post { - id Int @id @default(autoincrement()) - ... - //highlight-next-line - deleted Boolean @default(false) - } - ``` - -- Create a nullable `DateTime` field so that you know exactly _when_ a record was marked as deleted - `NULL` indicates that a record has not been deleted. In some cases, storing when a record was removed may be a regulatory requirement: - - ```prisma highlight=4;normal - model Post { - id Int @id @default(autoincrement()) - ... - //highlight-next-line - deleted DateTime? - } - ``` - -> **Note**: Using two separate fields (`isDeleted` and `deletedDate`) may result in these two fields becoming out of sync - for example, a record may be marked as deleted but have no associated date.) - -This sample uses a `Boolean` field type for simplicity. - -## Step 2: Soft delete middleware - -Add a middleware that performs the following tasks: - -- Intercepts `delete()` and `deleteMany()` queries for the `Post` model -- Changes the `params.action` to `update` and `updateMany` respectively -- Introduces a `data` argument and sets `{ deleted: true }`, preserving other filter arguments if they exist - -Run the following sample to test the soft delete middleware: - -```ts -import { PrismaClient } from '@prisma/client' - -const prisma = new PrismaClient({}) - -async function main() { - /***********************************/ - /* SOFT DELETE MIDDLEWARE */ - /***********************************/ - - prisma.$use(async (params, next) => { - // Check incoming query type - if (params.model == 'Post') { - if (params.action == 'delete') { - // Delete queries - // Change action to an update - params.action = 'update' - params.args['data'] = { deleted: true } - } - if (params.action == 'deleteMany') { - // Delete many queries - params.action = 'updateMany' - if (params.args.data != undefined) { - params.args.data['deleted'] = true - } else { - params.args['data'] = { deleted: true } - } - } - } - return next(params) - }) - - /***********************************/ - /* TEST */ - /***********************************/ - - const titles = [ - { title: 'How to create soft delete middleware' }, - { title: 'How to install Prisma' }, - { title: 'How to update a record' }, - ] - - console.log('\u001b[1;34mSTARTING SOFT DELETE TEST \u001b[0m') - console.log('\u001b[1;34m#################################### \u001b[0m') - - let i = 0 - let posts = new Array() - - // Create 3 new posts with a randomly assigned title each time - for (i == 0; i < 3; i++) { - const createPostOperation = prisma.post.create({ - data: titles[Math.floor(Math.random() * titles.length)], - }) - posts.push(createPostOperation) - } - - var postsCreated = await prisma.$transaction(posts) - - console.log( - 'Posts created with IDs: ' + - '\u001b[1;32m' + - postsCreated.map((x) => x.id) + - '\u001b[0m' - ) - - // Delete the first post from the array - const deletePost = await prisma.post.delete({ - where: { - id: postsCreated[0].id, // Random ID - }, - }) - - // Delete the 2nd two posts - const deleteManyPosts = await prisma.post.deleteMany({ - where: { - id: { - in: [postsCreated[1].id, postsCreated[2].id], - }, - }, - }) - - const getPosts = await prisma.post.findMany({ - where: { - id: { - in: postsCreated.map((x) => x.id), - }, - }, - }) - - console.log() - - console.log( - 'Deleted post with ID: ' + '\u001b[1;32m' + deletePost.id + '\u001b[0m' - ) - console.log( - 'Deleted posts with IDs: ' + - '\u001b[1;32m' + - [postsCreated[1].id + ',' + postsCreated[2].id] + - '\u001b[0m' - ) - console.log() - console.log( - 'Are the posts still available?: ' + - (getPosts.length == 3 - ? '\u001b[1;32m' + 'Yes!' + '\u001b[0m' - : '\u001b[1;31m' + 'No!' + '\u001b[0m') - ) - console.log() - console.log('\u001b[1;34m#################################### \u001b[0m') - // 4. Count ALL posts - const f = await prisma.post.findMany({}) - console.log('Number of posts: ' + '\u001b[1;32m' + f.length + '\u001b[0m') - - // 5. Count DELETED posts - const r = await prisma.post.findMany({ - where: { - deleted: true, - }, - }) - console.log( - 'Number of SOFT deleted posts: ' + '\u001b[1;32m' + r.length + '\u001b[0m' - ) -} - -main() -``` - -The sample outputs the following: - -```no-lines -STARTING SOFT DELETE TEST -#################################### -Posts created with IDs: 587,588,589 - -Deleted post with ID: 587 -Deleted posts with IDs: 588,589 - -Are the posts still available?: Yes! - -#################################### -``` - -:::tip - -Comment out the middleware to see the message change. - -::: - -✔ Pros of this approach to soft delete include: - -- Soft delete happens at data access level, which means that you cannot delete records unless you use raw SQL - -✘ Cons of this approach to soft delete include: - -- Content can still be read and updated unless you explicitly filter by `where: { deleted: false }` - in a large project with a lot of queries, there is a risk that soft deleted content will still be displayed -- You can still use raw SQL to delete records - -:::tip - -You can create rules or triggers ([MySQL](https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html) and [PostgreSQL](https://www.postgresql.org/docs/8.1/rules-update.html)) at a database level to prevent records from being deleted. - -::: - -## Step 3: Optionally prevent read/update of soft deleted records - -In step 2, we implemented middleware that prevents `Post` records from being deleted. However, you can still read and update deleted records. This step explores two ways to prevent the reading and updating of deleted records. - -> **Note**: These options are just ideas with pros and cons, you may choose to do something entirely different. - -### Option 1: Implement filters in your own application code - -In this option: - -- Prisma Client middleware is responsible for preventing records from being deleted -- Your own application code (which could be a GraphQL API, a REST API, a module) is responsible for filtering out deleted posts where necessary (`{ where: { deleted: false } }`) when reading and updating data - for example, the `getPost` GraphQL resolver never returns a deleted post - -✔ Pros of this approach to soft delete include: - -- No change to Prisma Client's create/update queries - you can easily request deleted records if you need them -- Modifying queries in middleware can have some unintended consequences, such as changing query return types (see option 2) - -✘ Cons of this approach to soft delete include: - -- Logic relating to soft delete maintained in two different places -- If your API surface is very large and maintained by multiple contributors, it may be difficult to enforce certain business rules (for example, never allow deleted records to be updated) - -### Option 2: Use middleware to determine the behavior of read/update queries for deleted records - -Option two uses Prisma Client middleware to prevent soft deleted records from being returned. The following table describes how the middleware affects each query: - -| **Query** | **Middleware logic** | **Changes to return type** | -| :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------- | -| `findUnique()` | 🔧 Change query to `findFirst` (because you cannot apply `deleted: false` filters to `findUnique()`)
🔧 Add `where: { deleted: false }` filter to exclude soft deleted posts
🔧 From version 5.0.0, you can use `findUnique()` to apply `delete: false` filters since [non unique fields are exposed](/orm/reference/prisma-client-reference#filter-on-non-unique-fields-with-userwhereuniqueinput). | No change | | -| `findMany` | 🔧 Add `where: { deleted: false }` filter to exclude soft deleted posts by default
🔧 Allow developers to **explicitly request** soft deleted posts by specifying `deleted: true` | No change | -| `update` | 🔧 Change query to `updateMany` (because you cannot apply `deleted: false` filters to `update`)
🔧 Add `where: { deleted: false }` filter to exclude soft deleted posts | `{ count: n }` instead of `Post` | -| `updateMany` | 🔧 Add `where: { deleted: false }` filter to exclude soft deleted posts | No change | - -- **Is it not possible to utilize soft delete with `findFirstOrThrow()` or `findUniqueOrThrow()`?**
- From version [5.1.0](https://github.com/prisma/prisma/releases/5.1.0), you can apply soft delete `findFirstOrThrow()` or `findUniqueOrThrow()` by using middleware. -- **Why are you making it possible to use `findMany()` with a `{ where: { deleted: true } }` filter, but not `updateMany()`?**
- This particular sample was written to support the scenario where a user can _restore_ their deleted blog post (which requires a list of soft deleted posts) - but the user should not be able to edit a deleted post. -- **Can I still `connect` or `connectOrCreate` a deleted post?**
- In this sample - yes. The middleware does not prevent you from connecting an existing, soft deleted post to a user. - -Run the following sample to see how middleware affects each query: - -```ts -import { PrismaClient, Prisma } from '@prisma/client' - -const prisma = new PrismaClient({}) - -async function main() { - /***********************************/ - /* SOFT DELETE MIDDLEWARE */ - /***********************************/ - - prisma.$use(async (params, next) => { - if (params.model == 'Post') { - if (params.action === 'findUnique' || params.action === 'findFirst') { - // Change to findFirst - you cannot filter - // by anything except ID / unique with findUnique() - params.action = 'findFirst' - // Add 'deleted' filter - // ID filter maintained - params.args.where['deleted'] = false - } - if ( - params.action === 'findFirstOrThrow' || - params.action === 'findUniqueOrThrow' - ) { - if (params.args.where) { - if (params.args.where.deleted == undefined) { - // Exclude deleted records if they have not been explicitly requested - params.args.where['deleted'] = false - } - } else { - params.args['where'] = { deleted: false } - } - } - if (params.action === 'findMany') { - // Find many queries - if (params.args.where) { - if (params.args.where.deleted == undefined) { - params.args.where['deleted'] = false - } - } else { - params.args['where'] = { deleted: false } - } - } - } - return next(params) - }) - - prisma.$use(async (params, next) => { - if (params.model == 'Post') { - if (params.action == 'update') { - // Change to updateMany - you cannot filter - // by anything except ID / unique with findUnique() - params.action = 'updateMany' - // Add 'deleted' filter - // ID filter maintained - params.args.where['deleted'] = false - } - if (params.action == 'updateMany') { - if (params.args.where != undefined) { - params.args.where['deleted'] = false - } else { - params.args['where'] = { deleted: false } - } - } - } - return next(params) - }) - - prisma.$use(async (params, next) => { - // Check incoming query type - if (params.model == 'Post') { - if (params.action == 'delete') { - // Delete queries - // Change action to an update - params.action = 'update' - params.args['data'] = { deleted: true } - } - if (params.action == 'deleteMany') { - // Delete many queries - params.action = 'updateMany' - if (params.args.data != undefined) { - params.args.data['deleted'] = true - } else { - params.args['data'] = { deleted: true } - } - } - } - return next(params) - }) - - /***********************************/ - /* TEST */ - /***********************************/ - - const titles = [ - { title: 'How to create soft delete middleware' }, - { title: 'How to install Prisma' }, - { title: 'How to update a record' }, - ] - - console.log('\u001b[1;34mSTARTING SOFT DELETE TEST \u001b[0m') - console.log('\u001b[1;34m#################################### \u001b[0m') - - let i = 0 - let posts = new Array() - - // Create 3 new posts with a randomly assigned title each time - for (i == 0; i < 3; i++) { - const createPostOperation = prisma.post.create({ - data: titles[Math.floor(Math.random() * titles.length)], - }) - posts.push(createPostOperation) - } - - var postsCreated = await prisma.$transaction(posts) - - console.log( - 'Posts created with IDs: ' + - '\u001b[1;32m' + - postsCreated.map((x) => x.id) + - '\u001b[0m' - ) - - // Delete the first post from the array - const deletePost = await prisma.post.delete({ - where: { - id: postsCreated[0].id, // Random ID - }, - }) - - // Delete the 2nd two posts - const deleteManyPosts = await prisma.post.deleteMany({ - where: { - id: { - in: [postsCreated[1].id, postsCreated[2].id], - }, - }, - }) - - const getOnePost = await prisma.post.findUnique({ - where: { - id: postsCreated[0].id, - }, - }) - - const getOneUniquePostOrThrow = async () => - await prisma.post.findUniqueOrThrow({ - where: { - id: postsCreated[0].id, - }, - }) - - const getOneFirstPostOrThrow = async () => - await prisma.post.findFirstOrThrow({ - where: { - id: postsCreated[0].id, - }, - }) - - const getPosts = await prisma.post.findMany({ - where: { - id: { - in: postsCreated.map((x) => x.id), - }, - }, - }) - - const getPostsAnDeletedPosts = await prisma.post.findMany({ - where: { - id: { - in: postsCreated.map((x) => x.id), - }, - deleted: true, - }, - }) - - const updatePost = await prisma.post.update({ - where: { - id: postsCreated[1].id, - }, - data: { - title: 'This is an updated title (update)', - }, - }) - - const updateManyDeletedPosts = await prisma.post.updateMany({ - where: { - deleted: true, - id: { - in: postsCreated.map((x) => x.id), - }, - }, - data: { - title: 'This is an updated title (updateMany)', - }, - }) - - console.log() - - console.log( - 'Deleted post (delete) with ID: ' + - '\u001b[1;32m' + - deletePost.id + - '\u001b[0m' - ) - console.log( - 'Deleted posts (deleteMany) with IDs: ' + - '\u001b[1;32m' + - [postsCreated[1].id + ',' + postsCreated[2].id] + - '\u001b[0m' - ) - console.log() - console.log( - 'findUnique: ' + - (getOnePost?.id != undefined - ? '\u001b[1;32m' + 'Posts returned!' + '\u001b[0m' - : '\u001b[1;31m' + - 'Post not returned!' + - '(Value is: ' + - JSON.stringify(getOnePost) + - ')' + - '\u001b[0m') - ) - try { - console.log('findUniqueOrThrow: ') - await getOneUniquePostOrThrow() - } catch (error) { - if ( - error instanceof Prisma.PrismaClientKnownRequestError && - error.code == 'P2025' - ) - console.log( - '\u001b[1;31m' + - 'PrismaClientKnownRequestError is catched' + - '(Error name: ' + - error.name + - ')' + - '\u001b[0m' - ) - } - try { - console.log('findFirstOrThrow: ') - await getOneFirstPostOrThrow() - } catch (error) { - if ( - error instanceof Prisma.PrismaClientKnownRequestError && - error.code == 'P2025' - ) - console.log( - '\u001b[1;31m' + - 'PrismaClientKnownRequestError is catched' + - '(Error name: ' + - error.name + - ')' + - '\u001b[0m' - ) - } - console.log() - console.log( - 'findMany: ' + - (getPosts.length == 3 - ? '\u001b[1;32m' + 'Posts returned!' + '\u001b[0m' - : '\u001b[1;31m' + 'Posts not returned!' + '\u001b[0m') - ) - console.log( - 'findMany ( delete: true ): ' + - (getPostsAnDeletedPosts.length == 3 - ? '\u001b[1;32m' + 'Posts returned!' + '\u001b[0m' - : '\u001b[1;31m' + 'Posts not returned!' + '\u001b[0m') - ) - console.log() - console.log( - 'update: ' + - (updatePost.id != undefined - ? '\u001b[1;32m' + 'Post updated!' + '\u001b[0m' - : '\u001b[1;31m' + - 'Post not updated!' + - '(Value is: ' + - JSON.stringify(updatePost) + - ')' + - '\u001b[0m') - ) - console.log( - 'updateMany ( delete: true ): ' + - (updateManyDeletedPosts.count == 3 - ? '\u001b[1;32m' + 'Posts updated!' + '\u001b[0m' - : '\u001b[1;31m' + 'Posts not updated!' + '\u001b[0m') - ) - console.log() - console.log('\u001b[1;34m#################################### \u001b[0m') - // 4. Count ALL posts - const f = await prisma.post.findMany({}) - console.log( - 'Number of active posts: ' + '\u001b[1;32m' + f.length + '\u001b[0m' - ) - - // 5. Count DELETED posts - const r = await prisma.post.findMany({ - where: { - deleted: true, - }, - }) - console.log( - 'Number of SOFT deleted posts: ' + '\u001b[1;32m' + r.length + '\u001b[0m' - ) -} - -main() -``` - -The sample outputs the following: - -``` -STARTING SOFT DELETE TEST -#################################### -Posts created with IDs: 680,681,682 - -Deleted post (delete) with ID: 680 -Deleted posts (deleteMany) with IDs: 681,682 - -findUnique: Post not returned!(Value is: []) -findMany: Posts not returned! -findMany ( delete: true ): Posts returned! - -update: Post not updated!(Value is: {"count":0}) -updateMany ( delete: true ): Posts not updated! - -#################################### -Number of active posts: 0 -Number of SOFT deleted posts: 95 -``` - -✔ Pros of this approach: - -- A developer can make a conscious choice to include deleted records in `findMany` -- You cannot accidentally read or update a deleted record - -✖ Cons of this approach: - -- Not obvious from API that you aren't getting all records and that `{ where: { deleted: false } }` is part of the default query -- Return type `update` affected because middleware changes the query to `updateMany` -- Doesn't handle complex queries with `AND`, `OR`, `every`, etc... -- Doesn't handle filtering when using `include` from another model. - -## FAQ - -### Can I add a global `includeDeleted` to the `Post` model? - -You may be tempted to 'hack' your API by adding a `includeDeleted` property to the `Post` model and make the following query possible: - -```ts -prisma.post.findMany({ where: { includeDeleted: true } }) -``` - -> **Note**: You would still need to write middleware. - -We **✘ do not** recommend this approach as it pollutes the schema with fields that do not represent real data. diff --git a/content/200-orm/200-prisma-client/300-client-extensions/500-middleware/200-logging-middleware.mdx b/content/200-orm/200-prisma-client/300-client-extensions/500-middleware/200-logging-middleware.mdx deleted file mode 100644 index b13eda09f9..0000000000 --- a/content/200-orm/200-prisma-client/300-client-extensions/500-middleware/200-logging-middleware.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: 'Middleware sample: logging' -metaTitle: 'Middleware sample: logging (Reference)' -metaDescription: 'How to use middleware to log the time taken to perform any query.' ---- - - - -The following example logs the time taken for a Prisma Client query to run: - -```ts -const prisma = new PrismaClient() - -prisma.$use(async (params, next) => { - const before = Date.now() - - const result = await next(params) - - const after = Date.now() - - console.log(`Query ${params.model}.${params.action} took ${after - before}ms`) - - return result -}) - -const create = await prisma.post.create({ - data: { - title: 'Welcome to Prisma Day 2020', - }, -}) - -const createAgain = await prisma.post.create({ - data: { - title: 'All about database collation', - }, -}) -``` - -Example output: - -```no-lines -Query Post.create took 92ms -Query Post.create took 15ms -``` - -The example is based on the following sample schema: - -```prisma -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "mysql" - url = env("DATABASE_URL") -} - -model Post { - authorId Int? - content String? - id Int @id @default(autoincrement()) - published Boolean @default(false) - title String - user User? @relation(fields: [authorId], references: [id]) - language String? - - @@index([authorId], name: "authorId") -} - -model User { - email String @unique - id Int @id @default(autoincrement()) - name String? - posts Post[] - extendedProfile Json? - role Role @default(USER) -} - -enum Role { - ADMIN - USER - MODERATOR -} -``` - - - -## Going further - -You can also use [Prisma Client extensions](/orm/prisma-client/client-extensions) to log the time it takes to perform a query. A functional example can be found in [this GitHub repository](https://github.com/prisma/prisma-client-extensions/tree/main/query-logging). diff --git a/content/200-orm/200-prisma-client/300-client-extensions/500-middleware/300-session-data-middleware.mdx b/content/200-orm/200-prisma-client/300-client-extensions/500-middleware/300-session-data-middleware.mdx deleted file mode 100644 index 6893713396..0000000000 --- a/content/200-orm/200-prisma-client/300-client-extensions/500-middleware/300-session-data-middleware.mdx +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: 'Middleware sample: session data' -metaTitle: 'Middleware sample: session data (Reference)' -metaDescription: 'How to use middleware to set the value taken from session state.' -hide_table_of_contents: true ---- - - - -The following example sets the `language` field of each `Post` to the context language (taken, for example, from session state): - -```ts -const prisma = new PrismaClient() - -const contextLanguage = 'en-us' // Session state - -prisma.$use(async (params, next) => { - if (params.model == 'Post' && params.action == 'create') { - params.args.data.language = contextLanguage - } - - return next(params) -}) - -const create = await prisma.post.create({ - data: { - title: 'My post in English', - }, -}) -``` - -The example is based on the following sample schema: - -```prisma -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "mysql" - url = env("DATABASE_URL") -} - -model Post { - authorId Int? - content String? - id Int @id @default(autoincrement()) - published Boolean @default(false) - title String - user User? @relation(fields: [authorId], references: [id]) - language String? - - @@index([authorId], name: "authorId") -} - -model User { - email String @unique - id Int @id @default(autoincrement()) - name String? - posts Post[] - extendedProfile Json? - role Role @default(USER) -} - -enum Role { - ADMIN - USER - MODERATOR -} -``` - - diff --git a/content/200-orm/200-prisma-client/300-client-extensions/500-middleware/index.mdx b/content/200-orm/200-prisma-client/300-client-extensions/500-middleware/index.mdx deleted file mode 100644 index 9ce688e592..0000000000 --- a/content/200-orm/200-prisma-client/300-client-extensions/500-middleware/index.mdx +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: 'Middleware' -metaTitle: 'Middleware (Reference)' -metaDescription: 'Prisma Client middleware allows you to perform actions before or after any query on any model with the prisma.$use method.' ---- - - - - - -**Middleware has been removed in [v6.14.0](https://pris.ly/release/6.14.0)** and had been deprecated since [v4.16.0](https://github.com/prisma/prisma/releases/tag/4.16.0). - -We recommend using the [Prisma Client extensions `query` component type](/orm/prisma-client/client-extensions/query) as an alternative to middleware. Prisma Client extensions were first introduced into Preview in version 4.7.0 and made Generally Available in 4.16.0. - -Prisma Client extensions allow you to create independent Prisma Client instances and bind each client to a specific filter or user. For example, you could bind clients to specific users to provide user isolation. Prisma Client extensions also provide end-to-end type safety. - - - -Middlewares act as query-level lifecycle hooks, which allow you to perform an action before or after a query runs. Use the [`prisma.$use`](/orm/reference/prisma-client-reference#use) method to add middleware, as follows: - -```ts highlight=4-9,12-17;normal -const prisma = new PrismaClient() - -// Middleware 1 -//highlight-start -prisma.$use(async (params, next) => { - // Manipulate params here - const result = await next(params) - // See results here - return result -}) -//highlight-end - -// Middleware 2 -//highlight-start -prisma.$use(async (params, next) => { - // Manipulate params here - const result = await next(params) - // See results here - return result -}) -//highlight-end - -// Queries here -``` - - - -Do not invoke `next` multiple times within a middleware when using [batch transactions](/orm/prisma-client/queries/transactions#sequential-prisma-client-operations). This will cause you to break out of the transaction and lead to unexpected results. - - - -[`params`](/orm/reference/prisma-client-reference#params) represent parameters available in the middleware, such as the name of the query, and [`next`](/orm/reference/prisma-client-reference#next) represents [the next middleware in the stack _or_ the original Prisma Client query](#running-order-and-the-middleware-stack). - -Possible use cases for middleware include: - -- Setting or overwriting a field value - for example, [setting the context language of a blog post comment](/orm/prisma-client/client-extensions/middleware/session-data-middleware) -- Validating input data - for example, check user input for inappropriate language via an external service -- Intercept a `delete` query and change it to an `update` in order to perform a [soft delete](/orm/prisma-client/client-extensions/middleware/soft-delete-middleware) -- [Log the time taken to perform a query](/orm/prisma-client/client-extensions/middleware/logging-middleware) - -There are many more use cases for middleware - this list serves as inspiration for the types of problems that middleware is designed to address. - - - -## Samples - -The following sample scenarios show how to use middleware in practice: - - - -## Where to add middleware - -Add Prisma Client middleware **outside the context of the request handler**, otherwise each request adds a new _instance_ of the middleware to the stack. The following example demonstrates where to add Prisma Client middleware in the context of an Express app: - -```ts highlight=6-11;normal -import express from 'express' -import { PrismaClient } from '@prisma/client' - -const prisma = new PrismaClient() - -//highlight-start -prisma.$use(async (params, next) => { - // Manipulate params here - const result = await next(params) - // See results here - return result -}) -//highlight-end - -const app = express() -app.get('/feed', async (req, res) => { - // NO MIDDLEWARE HERE - const posts = await prisma.post.findMany({ - where: { published: true }, - include: { author: true }, - }) - res.json(posts) -}) -``` - -## Running order and the middleware stack - -If you have multiple middlewares, the running order for **each separate query** is: - -1. All logic **before** `await next(params)` in each middleware, in descending order -2. All logic **after** `await next(params)` in each middleware, in ascending order - -Depending on where you are in the stack, `await next(params)` either: - -- Runs the next middleware (in middlewares #1 and #2 in the example) _or_ -- Runs the original Prisma Client query (in middleware #3) - -```ts -const prisma = new PrismaClient() - -// Middleware 1 -prisma.$use(async (params, next) => { - console.log(params.args.data.title) - console.log('1') - const result = await next(params) - console.log('6') - return result -}) - -// Middleware 2 -prisma.$use(async (params, next) => { - console.log('2') - const result = await next(params) - console.log('5') - return result -}) - -// Middleware 3 -prisma.$use(async (params, next) => { - console.log('3') - const result = await next(params) - console.log('4') - return result -}) - -const create = await prisma.post.create({ - data: { - title: 'Welcome to Prisma Day 2020', - }, -}) - -const create2 = await prisma.post.create({ - data: { - title: 'How to Prisma!', - }, -}) -``` - -Output: - -```no-lines -Welcome to Prisma Day 2020 -1 -2 -3 -4 -5 -6 -How to Prisma! -1 -2 -3 -4 -5 -6 -``` - -## Performance and appropriate use cases - -Middleware executes for **every** query, which means that overuse has the potential to negatively impact performance. To avoid adding performance overheads: - -- Check the `params.model` and `params.action` properties early in your middleware to avoid running logic unnecessarily: - - ```ts - prisma.$use(async (params, next) => { - if (params.model == 'Post' && params.action == 'delete') { - // Logic only runs for delete action and Post model - } - return next(params) - }) - ``` - -- Consider whether middleware is the appropriate solution for your scenario. For example: - - - If you need to populate a field, can you use the [`@default`](/orm/reference/prisma-schema-reference#default) attribute? - - If you need to set the value of a `DateTime` field, can you use the `now()` function or the `@updatedAt` attribute? - - If you need to perform more complex validation, can you use a `CHECK` constraint in the database itself? diff --git a/content/200-orm/200-prisma-client/300-client-extensions/index.mdx b/content/200-orm/200-prisma-client/300-client-extensions/index.mdx index cfcf31b5e2..d0bb657074 100644 --- a/content/200-orm/200-prisma-client/300-client-extensions/index.mdx +++ b/content/200-orm/200-prisma-client/300-client-extensions/index.mdx @@ -230,32 +230,6 @@ The `Prisma.Result` type utility is used to infer the type of the extended `User ## Limitations -### Usage of `$on` and `$use` with extended clients - -`$on` and `$use` are not available in extended clients. If you would like to continue using these [client-level methods](/orm/reference/prisma-client-reference#client-methods) with an extended client, you will need to hook them up before extending the client. - -```ts -const prisma = new PrismaClient() - -prisma.$use(async (params, next) => { - console.log('This is middleware!') - return next(params) -}) - -const xPrisma = prisma.$extends({ - name: 'myExtension', - model: { - user: { - async signUp(email: string) { - await prisma.user.create({ data: { email } }) - }, - }, - }, -}) -``` - -To learn more, see our documentation on [`$on`](/orm/reference/prisma-client-reference#on) and [`$use`](/orm/reference/prisma-client-reference#use) - ### Usage of client-level methods in extended clients [Client-level methods](/orm/reference/prisma-client-reference#client-methods) do not necessarily exist on extended clients. For these clients you will need to first check for existence before using. diff --git a/content/200-orm/200-prisma-client/600-observability-and-logging/250-opentelemetry-tracing.mdx b/content/200-orm/200-prisma-client/600-observability-and-logging/250-opentelemetry-tracing.mdx index 87230c0b81..f1b866c7d3 100644 --- a/content/200-orm/200-prisma-client/600-observability-and-logging/250-opentelemetry-tracing.mdx +++ b/content/200-orm/200-prisma-client/600-observability-and-logging/250-opentelemetry-tracing.mdx @@ -181,9 +181,7 @@ const sdk = new NodeSDK({ serviceName: 'my-service-name', // Replace with your service name traceExporter, instrumentations: [ - new PrismaInstrumentation({ - middleware: true, // Enable middleware tracing if needed - }), + new PrismaInstrumentation(), ], }) @@ -285,20 +283,6 @@ export function otelSetup() { } ``` -### Trace Prisma Client middleware - -By default, tracing does not output spans for [Prisma Client middleware](/orm/prisma-client/client-extensions/middleware). To include your middleware in your traces, set `middleware` to `true` in your `registerInstrumentations` statement, as follows: - -```ts -registerInstrumentations({ - instrumentations: [new PrismaInstrumentation({ middleware: true })], -}) -``` - -This will add the following span type to your traces: - -- `prisma:client:middleware`: Represents how long the operation spent in your [middleware](/orm/prisma-client/client-extensions/middleware). - ### Trace interactive transactions When you perform an interactive transaction, you'll see the following spans in addition to the [standard spans](#trace-output): diff --git a/content/200-orm/500-reference/050-prisma-client-reference.mdx b/content/200-orm/500-reference/050-prisma-client-reference.mdx index a9fe8c5afd..cc31ef781c 100644 --- a/content/200-orm/500-reference/050-prisma-client-reference.mdx +++ b/content/200-orm/500-reference/050-prisma-client-reference.mdx @@ -5514,63 +5514,6 @@ The `$connect()` method establishes a physical connection to the database via Pr The `$on()` method allows you to subscribe to [logging events](#log) or the [exit hook](/orm/prisma-client/setup-and-configuration/databases-connections/connection-management#exit-hooks). -### `$use()` - - - -`$use` is not available in [extended clients](/orm/prisma-client/client-extensions). Please [either migrate to query extensions](/orm/prisma-client/client-extensions/query) or use the `$use` method prior to extending your client. - - - -The `$use()` method adds [middleware](/orm/prisma-client/client-extensions/middleware) : - -```ts -prisma.$use(async (params, next) => { - console.log('This is middleware!'); - // Modify or interrogate params here - - return next(params); -}); -``` - -#### `next` - -`next` represents the "next level" in the middleware stack, which could be the next middleware or the Prisma Query, depending on [where in the stack you are](/orm/prisma-client/client-extensions/middleware#running-order-and-the-middleware-stack). - -#### `params` - -`params` is an object with information to use in your middleware. - -| Parameter | Description | -| :----------------- | :--------------------------------------------------------------------------------------------- | -| `action` | The query type - for example, `create` or `findMany`. | -| `args` | Arguments that were passed into the query - for example, `where`, `data`, or `orderBy` | -| `dataPath` | Populated if you use the [fluent API](/orm/prisma-client/queries/relation-queries#fluent-api). | -| `model` | The model type - for example, `Post` or `User`. | -| `runInTransaction` | Returns `true` if the query ran in the context of a [transaction](#transaction). | - -:::tip - -If you need the `model` property as a string, use: `String(params.model)` - -::: - -Example parameter values: - -```js -{ - args: { where: { id: 15 } }, - dataPath: [ 'select', 'author', 'select', 'posts' ], - runInTransaction: false, - action: 'findMany', - model: 'Post' -} -``` - -#### Examples - -See [middleware examples](/orm/prisma-client/client-extensions/middleware#samples). - ### `$queryRawTyped` See: [Using Raw SQL (`$queryRawTyped`)](/orm/prisma-client/using-raw-sql/typedsql). diff --git a/content/200-orm/500-reference/500-preview-features/050-client-preview-features.mdx b/content/200-orm/500-reference/500-preview-features/050-client-preview-features.mdx index 469421c32c..1ea24923c5 100644 --- a/content/200-orm/500-reference/500-preview-features/050-client-preview-features.mdx +++ b/content/200-orm/500-reference/500-preview-features/050-client-preview-features.mdx @@ -12,16 +12,16 @@ For more information, see [ORM releases and maturity levels](/orm/more/releases) The following [Preview](/orm/more/releases#preview) feature flags are available for Prisma Client and Prisma schema: -| Feature | Released into Preview | Feedback issue | -| ------------------------------------------------------------------------------- | :------------------------------------------------------------- | :-------------------------------------------------------------------: | -| [`metrics`](/orm/prisma-client/observability-and-logging/metrics) | [3.15.0](https://github.com/prisma/prisma/releases/tag/3.15.0) | [Submit feedback](https://github.com/prisma/prisma/issues/13579) | -| [`views`](/orm/prisma-schema/data-model/views) | [4.9.0](https://github.com/prisma/prisma/releases/tag/4.9.0) | [Submit feedback](https://github.com/prisma/prisma/issues/17335) | -| `relationJoins` | [5.7.0](https://github.com/prisma/prisma/releases/tag/5.7.0) | [Submit feedback](https://github.com/prisma/prisma/discussions/22288) | -| `nativeDistinct` | [5.7.0](https://github.com/prisma/prisma/releases/tag/5.7.0) | [Submit feedback](https://github.com/prisma/prisma/discussions/22287) | -| `typedSql` | [5.19.0](https://github.com/prisma/prisma/releases/tag/5.19.0) | [Submit feedback](https://github.com/prisma/prisma/discussions/25106) | -| `strictUndefinedChecks` | [5.20.0](https://github.com/prisma/prisma/releases/tag/5.20.0) | [Submit feedback](https://github.com/prisma/prisma/discussions/25271) | -| [`fullTextSearchPostgres`](/orm/prisma-client/queries/full-text-search) | [6.0.0](https://github.com/prisma/prisma/releases/tag/6.0.0) | [Submit feedback](https://github.com/prisma/prisma/issues/25773) | -| `shardKeys` | [6.10.0](https://pris.ly/release/6.10.0) | [Submit feedback](https://github.com/prisma/prisma/issues/) | +| Feature | Released into Preview | Feedback issue | +| ----------------------------------------------------------------------- | :------------------------------------------------------------- | :-------------------------------------------------------------------: | +| [`metrics`](/orm/prisma-client/observability-and-logging/metrics) | [3.15.0](https://github.com/prisma/prisma/releases/tag/3.15.0) | [Submit feedback](https://github.com/prisma/prisma/issues/13579) | +| [`views`](/orm/prisma-schema/data-model/views) | [4.9.0](https://github.com/prisma/prisma/releases/tag/4.9.0) | [Submit feedback](https://github.com/prisma/prisma/issues/17335) | +| `relationJoins` | [5.7.0](https://github.com/prisma/prisma/releases/tag/5.7.0) | [Submit feedback](https://github.com/prisma/prisma/discussions/22288) | +| `nativeDistinct` | [5.7.0](https://github.com/prisma/prisma/releases/tag/5.7.0) | [Submit feedback](https://github.com/prisma/prisma/discussions/22287) | +| `typedSql` | [5.19.0](https://github.com/prisma/prisma/releases/tag/5.19.0) | [Submit feedback](https://github.com/prisma/prisma/discussions/25106) | +| `strictUndefinedChecks` | [5.20.0](https://github.com/prisma/prisma/releases/tag/5.20.0) | [Submit feedback](https://github.com/prisma/prisma/discussions/25271) | +| [`fullTextSearchPostgres`](/orm/prisma-client/queries/full-text-search) | [6.0.0](https://github.com/prisma/prisma/releases/tag/6.0.0) | [Submit feedback](https://github.com/prisma/prisma/issues/25773) | +| `shardKeys` | [6.10.0](https://pris.ly/release/6.10.0) | [Submit feedback](https://github.com/prisma/prisma/issues/) | To enable a Preview feature, [add the feature flag to the `generator` block](#enabling-a-prisma-client-preview-feature) in your `schema.prisma` file. [Share your feedback on all Preview features on GitHub](https://github.com/prisma/prisma/issues/3108). @@ -86,6 +86,5 @@ In the list below, you can find a history of Prisma Client and Prisma schema fea | [`connectOrCreate`](/orm/reference/prisma-client-reference#connectorcreate) | [2.1.0](https://github.com/prisma/prisma/releases/tag/2.1.0) | [2.11.0](https://github.com/prisma/prisma/releases/tag/2.11.0) | | [`atomicNumberOperations`](/orm/reference/prisma-client-reference#atomic-number-operations) | [2.6.0](https://github.com/prisma/prisma/releases/tag/2.6.0) | [2.10.0](https://github.com/prisma/prisma/releases/tag/2.10.0) | | [`insensitiveFilters` (PostgreSQL)](/orm/prisma-client/queries/filtering-and-sorting#case-insensitive-filtering) | [2.5.0](https://github.com/prisma/prisma/releases/tag/2.5.0) | [2.8.0](https://github.com/prisma/prisma/releases/tag/2.8.0) | -| [`middlewares`](/orm/prisma-client/client-extensions/middleware) | [2.3.0](https://github.com/prisma/prisma/releases/tag/2.3.0) | [2.5.0](https://github.com/prisma/prisma/releases/tag/2.5.0) | | [`aggregateApi`](/orm/prisma-client/queries/aggregation-grouping-summarizing#aggregate) | [2.2.0](https://github.com/prisma/prisma/releases/tag/2.2.0) | [2.5.0](https://github.com/prisma/prisma/releases/tag/2.5.0) | | [`distinct`](/orm/reference/prisma-client-reference#distinct) | [2.3.0](https://github.com/prisma/prisma/releases/tag/2.3.0) | [2.5.0](https://github.com/prisma/prisma/releases/tag/2.5.0) | diff --git a/content/200-orm/800-more/350-ai-tools/100-cursor.mdx b/content/200-orm/800-more/350-ai-tools/100-cursor.mdx index 9d8275a356..2ab60d89ab 100644 --- a/content/200-orm/800-more/350-ai-tools/100-cursor.mdx +++ b/content/200-orm/800-more/350-ai-tools/100-cursor.mdx @@ -105,10 +105,6 @@ Schema Design Prisma Client Usage - Always use type-safe Prisma client operations. - Prefer transactions for complex, multi-step operations. -- Use Prisma middleware for cross-cutting concerns: - - Logging - - Soft delete - - Auditing - Handle optional relations explicitly. - Use Prisma's filtering and pagination capabilities. Database Migrations diff --git a/content/200-orm/800-more/350-ai-tools/300-windsurf.mdx b/content/200-orm/800-more/350-ai-tools/300-windsurf.mdx index b312ff8758..814c77f782 100644 --- a/content/200-orm/800-more/350-ai-tools/300-windsurf.mdx +++ b/content/200-orm/800-more/350-ai-tools/300-windsurf.mdx @@ -110,10 +110,6 @@ Schema Design Prisma Client Usage - Always use type-safe Prisma client operations. - Prefer transactions for complex, multi-step operations. -- Use Prisma middleware for cross-cutting concerns: - - Logging - - Soft delete - - Auditing - Handle optional relations explicitly. - Use Prisma's filtering and pagination capabilities. Database Migrations diff --git a/content/200-orm/800-more/350-ai-tools/400-github-copilot.mdx b/content/200-orm/800-more/350-ai-tools/400-github-copilot.mdx index b36b38026d..740f3b9305 100644 --- a/content/200-orm/800-more/350-ai-tools/400-github-copilot.mdx +++ b/content/200-orm/800-more/350-ai-tools/400-github-copilot.mdx @@ -206,7 +206,6 @@ You can tailor Copilot Chat's behavior in your repository by [adding a `.github/ new PrismaClient({ log: ['query', 'warn', 'error'] }); /``` * **APM integration** (Datadog, Sentry) – capture latency, errors. -* **Client extensions** for metrics: create extensions that wrap calls to emit timing and telemetry instead of middleware. ### 11. Security & Best Practices diff --git a/content/250-postgres/400-query-optimization/100-setup.mdx b/content/250-postgres/400-query-optimization/100-setup.mdx index c5d87ea33d..c4737247b1 100644 --- a/content/250-postgres/400-query-optimization/100-setup.mdx +++ b/content/250-postgres/400-query-optimization/100-setup.mdx @@ -75,18 +75,14 @@ const prisma = new PrismaClient().$extends( ).$extends(withAccelerate()); ``` -#### Using the Optimize extension with other extensions or middleware +#### Using the Optimize extension with other extensions Since [extensions are applied one after another](/orm/prisma-client/client-extensions#conflicts-in-combined-extensions), make sure you apply them in the correct order. Extensions cannot share behavior and the last extension applied takes precedence. ```ts -const prisma = new PrismaClient().$extends(withOptimize()).$extends(withAccelerate()) -``` - -If you are using [Prisma Middleware](/orm/prisma-client/client-extensions/middleware) in your application, make sure they are added before any Prisma Client extensions (like Optimize). For example: - -```ts -const prisma = new PrismaClient().$use(middleware).$extends(withOptimize()).$extends(withAccelerate()) +const prisma = new PrismaClient() + .$extends(withOptimize()) + .$extends(withAccelerate()) ``` ### 2.5. Use Prisma Optimize to generate insights diff --git a/content/300-accelerate/200-getting-started.mdx b/content/300-accelerate/200-getting-started.mdx index 3452f94539..b06abc67a4 100644 --- a/content/300-accelerate/200-getting-started.mdx +++ b/content/300-accelerate/200-getting-started.mdx @@ -148,7 +148,7 @@ const prisma = new PrismaClient().$extends(withAccelerate()) If VS Code does not recognize the `$extends` method, refer to [this section](/accelerate/faq#vs-code-does-not-recognize-the-extends-method) on how to resolve the issue. -#### Using the Accelerate extension with other extensions or middleware +#### Using the Accelerate extension with other extensions Since [extensions are applied one after another](/orm/prisma-client/client-extensions#conflicts-in-combined-extensions), make sure you apply them in the correct order. Extensions cannot share behavior and the last extension applied takes precedence. @@ -158,12 +158,6 @@ If you are using [Prisma Optimize](/optimize) in your application, make sure you const prisma = new PrismaClient().$extends(withOptimize()).$extends(withAccelerate()) ``` -If you are using [Prisma Middleware](/orm/prisma-client/client-extensions/middleware) in your application, make sure they are added before any Prisma Client extensions (like Accelerate). For example: - -```ts -const prisma = new PrismaClient().$use(middleware).$extends(withAccelerate()) -``` - ### 2.5. Use Accelerate in your database queries The `withAccelerate` extension primarily does two things: diff --git a/content/700-optimize/200-getting-started.mdx b/content/700-optimize/200-getting-started.mdx index d3e4fa9078..cb67d6a86d 100644 --- a/content/700-optimize/200-getting-started.mdx +++ b/content/700-optimize/200-getting-started.mdx @@ -70,7 +70,7 @@ const prisma = new PrismaClient().$extends( ); ``` -#### Using the Optimize extension with other extensions or middleware +#### Using the Optimize extension with other extensions Since [extensions are applied one after another](/orm/prisma-client/client-extensions#conflicts-in-combined-extensions), make sure you apply them in the correct order. Extensions cannot share behavior and the last extension applied takes precedence. @@ -80,12 +80,6 @@ If you are using [Prisma Accelerate](/accelerate) in your application, make sure const prisma = new PrismaClient().$extends(withOptimize()).$extends(withAccelerate()) ``` -If you are using [Prisma Middleware](/orm/prisma-client/client-extensions/middleware) in your application, make sure they are added before any Prisma Client extensions (like Optimize). For example: - -```ts -const prisma = new PrismaClient().$use(middleware).$extends(withOptimize()) -``` - ### 2.5. Use Prisma Optimize to generate insights Follow these steps to start generating query insights with Prisma Optimize: