prisma client extentionのdocsを読む
これです
When you use a Prisma Client extension, you create an extended client. An extended client is a lightweight variant of the standard Prisma Client that is wrapped by one or more extensions.
lightweightらしい
Add a user.current() method for the User model to get the currently logged-in user.
利用例わかりやすい
この書き方が良さそう
import { Prisma } from '@prisma/client'
// Define the extension
const myExtension = Prisma.defineExtension({
name: 'signUp', // Optional: name appears in error logs
model: { // This is a `model` component
user: { ... } // The extension logic for the `user` model goes inside the curly braces
},
})
// Pass the extension to a Prisma Client instance
const xprisma = prisma.$extends(myExtension)
連鎖ができるので
// First of all, store your original Prisma Client in a variable as usual
const prisma = new PrismaClient()
// Declare an extended client that has an extensionA and extensionB
const prismaAB = prisma.$extends(extensionA).$extends(extensionB)
singleton作って中で連鎖させるのが良さそう
function getExtendedClient() {
return new PrismaClient().$extends({
/* extension */
})
}
type ExtendedPrismaClient = ReturnType<typeof getExtendedClient>
When you combine two or more extensions into a single extended client, then the last extension that you declare takes precedence in any conflict. In the example in option 1 above, suppose there is a method called myExtensionMethod() defined in extensionA and a method called myExtensionMethod() in extensionB. When you call prismaAB.myExtensionMethod(), then Prisma Client uses myExtensionMethod() as defined in extensionB.
競合したら後ろの方で上書きされるらしい
拡張対象について
model
用途。今の時点では区別のイメージそんな付いてない。
Possible uses for the model component include the following:
- New operations to operate alongside existing Prisma Client operations, such as findMany
- Encapsulated business logic
- Repetitive operations
- Model-specific utilities
あーこういうことか、なるほど
const prisma = new PrismaClient().$extends({
model: {
user: {
async signUp(email: string) {
await prisma.user.create({ data: { email } })
},
},
},
})
これもたしかに
const prisma = new PrismaClient().$extends({
model: {
$allModels: {
async exists<T>(
this: T,
where: Prisma.Args<T, 'findFirst'>['where']
): Promise<boolean> {
// Get the current model at runtime
const context = Prisma.getExtensionContext(this)
const result = await (context as any).findFirst({ where })
return result !== null
},
},
},
})
client
ほーん?metricsなんてついてたんだ
const prisma = new PrismaClient().$extends({
client: {
$log: (s: string) => console.log(s),
async $totalQueries() {
const index_prisma_client_queries_total = 0
// Prisma.getExtensionContext(this) in the following block
// returns the current client instance
const metricsCounters = await (
await Prisma.getExtensionContext(this).$metrics.json()
).counters
return metricsCounters[index_prisma_client_queries_total].value
},
},
})
async function main() {
prisma.$log('Hello world')
const totalQueries = await prisma.$totalQueries()
console.log(totalQueries)
}
今度さわってみよ
result
新しいfield生やすやつね
const prisma = new PrismaClient().$extends({
result: {
user: {
fullName: {
// the dependencies
needs: { firstName: true, lastName: true },
compute(user) {
// the computation logic
return `${user.firstName} ${user.lastName}`
},
},
},
},
})
const user = await prisma.user.findFirst()
// return the user's full name, such as "John Doe"
console.log(user.fullName)
Considerations for fields
- For performance reasons, Prisma Client computes results on access, not on retrieval.
- You can only create computed fields that are based on scalar fields.
- You can only use computed fields with select and you cannot aggregate them. For example:
アクセス時算出は覚えておいたほうがいいかも
modelとの使い分けどうなってんだろ
const prisma = new PrismaClient().$extends({
result: {
user: {
save: {
needs: { id: true },
compute(user) {
return () =>
prisma.user.update({ where: { id: user.id }, data: user })
},
},
},
},
})
const user = await prisma.user.findUniqueOrThrow({ where: { id: someId } })
user.email = 'mynewmail@mailservice.com'
await user.save()
query
なるほど、そんなメタ拡張が
const prisma = new PrismaClient().$extends({
query: {
async $allOperations({ operation, model, args, query }) {
const start = performance.now()
const result = await query(args)
const end = performance.now()
const time = end - start
console.log(
util.inspect(
{ model, operation, args, time },
{ showHidden: false, depth: null, colors: true }
)
)
return result
},
},
})
We include the above example to show that this is possible. However, for performance reasons we recommend that you use the result component type to override existing fields. The result component type usually gives better performance in this situation because it computes only on access. The query component type computes after query execution.
fieldの上書きはresultを拡張しといてアクセス後計算にしとくほうがパフォーマンスの観点から好ましい。