Overview
Client extensions are the supported mechanism for cross-cutting behavior in Prisma. prisma.$extends() returns a new client that layers extra behavior over the base client through four components: query, result, model, and client. Extensions replaced the $use middleware API, which was deprecated in v4.16.0 and removed in v6.14.0; it does not exist in Prisma 7. Use extensions for soft-delete filters, computed fields, audit logging, and custom helper methods. For the client lifecycle that holds these extensions, see prisma-client.
Assign the extended client; the base is immutable
$extends does not mutate the client. It returns a new instance with the extension applied.
const prisma = base.$extends({
/* components */
})- Export and use the extended
prisma, notbase. Queries onbaseskip the extension. - Chain
$extendscalls to compose extensions. Each layer wraps the previous one in order. - Type inference flows through the chain, so custom methods and computed fields are fully typed downstream. See typescript.
Use the query component to intercept and rewrite queries
The query component runs around each operation. It is the soft-delete and tenant-isolation tool that $use used to provide.
const prisma = base.$extends({
query: {
post: {
findMany({ args, query }) {
args.where = { ...args.where, deletedAt: null }
return query(args)
},
},
},
})- Scope hooks to a model and operation (
post.findMany) or to$allOperationsacross a model. - Call
query(args)exactly once to forward to the database. Mutateargsbefore the call to filter or default values. - Do not cache inside a
queryhook. It fires on every matching call, which makes invalidation unpredictable.
Use the result component to add computed fields
The result component adds derived fields without a database round trip.
const prisma = base.$extends({
result: {
user: {
fullName: {
needs: { firstName: true, lastName: true },
compute(user) {
return `${user.firstName} ${user.lastName}`
},
},
},
},
})needsdeclares the columns the computed field depends on. Prisma selects them automatically when the field is read.- Computed fields are virtual; they are not stored and cannot be filtered on in
where. Promote to a real column when you need to query it. See prisma-schema.
Use model and client for custom methods
The model component adds methods to a specific model; client adds top-level methods to the client.
const prisma = base.$extends({
model: {
user: {
async signUp(email: string) {
return base.user.create({ data: { email } })
},
},
},
client: {
$health() {
return base.$queryRaw`SELECT 1`
},
},
})- Use
modelmethods for domain operations that belong to one entity (user.signUp). - Use
clientmethods for cross-cutting helpers (health checks, raw maintenance queries). See prisma-raw-queries. - Extensions interact with transactions: inside an interactive
$transaction, thetxclient carries the same extensions. See prisma-transactions.