برنامه های افزودنی مشتری Prisma: از موارد و مشکلات استفاده کنید

اگرچه هنوز آزمایشی است، اما برنامه های افزودنی مشتری یکی از هیجان انگیزترین ویژگی های معرفی شده در نسخه های اخیر Prisma است. چرا؟ زیرا دری را برای توسعه دهندگان باز می کند تا رفتارهای سفارشی را به آن تزریق کنند PrismaClient
با انعطاف پذیری زیاد این پست چند سناریو جالب را نشان می دهد که توسط این ویژگی فعال شده است، همراه با افکاری در مورد اینکه کجا باید مرز تعیین کنیم تا از استفاده بیش از حد از قدرت آن جلوگیری کنیم.
زمینه
قبل از معرفی برنامههای افزودنی کلاینت، میانافزار تنها راه برای گسترش عملکرد اصلی زمان اجرا Prisma بود – میتوانید از آن برای ایجاد تغییرات در آرگومانهای پرس و جو و تغییر نتیجه استفاده کنید. افزونه های مشتری به عنوان جایگزینی در آینده برای میان افزارها با انعطاف پذیری بیشتر و ایمنی نوع ایجاد می شوند. در اینجا لیست سریعی از کارهایی است که می توانید با آن انجام دهید:
- یک روش سفارشی به مدل اضافه کنید
const xprisma = prisma.$extends({
model: {
user: {
async signUp(email: string) {
return prisma.user.create({ data: { email } });
},
},
},
});
const user = await xprisma.user.signUp('john@prisma.io');
- یک روش سفارشی به مشتری اضافه کنید
const xprisma = prisma.$extends({
client: {
$log: (s: string) => console.log(s),
},
});
prisma.$log('Hello world');
- یک فیلد سفارشی به نتیجه پرس و جو اضافه کنید
const xprisma = prisma.$extends({
result: {
user: {
fullName: {
// the dependencies
needs: { firstName: true, lastName: true },
compute(user) {
// the computation logic
return `${user.firstName} ${user.lastName}`;
},
},
},
},
});
const user = await xprisma.user.findFirst();
console.log(user.fullName);
const xprisma = prisma.$extends({
query: {
user: {
async findMany({ model, operation, args, query }) {
// inject an extra "age" filter
args.where = { age: { gt: 18 }, ...args.where };
return query(args);
},
},
},
});
await xprisma.user.findMany(); // returns users whose age is greater than 18
موارد استفاده
برنامه های افزودنی مشتری برای حل نگرانی های متقابل عالی هستند. در اینجا چند مورد استفاده برای تحریک الهام شما آورده شده است.
1. حذف نرم
حذف نرم روشی محبوب برای مدیریت حذف با قرار دادن نشانگر روی موجودیت ها بدون حذف واقعی آنها است تا در صورت لزوم بتوان داده ها را به سرعت بازیابی کرد. این به قدری تمایل دارد که در GitHub Prisma یک مشکل طولانی مدت در مورد آن وجود دارد – حذف های نرم (مثلاً deleted_at) #3398.
با افزونه های مشتری، می توانید حذف نرم را در یک مکان مرکزی پیاده سازی کنید. به عنوان مثال، فرض کنید شما طرحی مانند این دارید:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
deleted Boolean @default(false)
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
author User? @relation(fields: [authorId], references: [id])
authorId Int?
deleted Boolean @default(false)
}
حذف نرم به صورت زیر قابل پیاده سازی است:
const xprisma = prisma.$extends({
name: 'soft-delete',
query: {
$allModels: {
async findMany({ args, query }) {
// inject read filter
args.where = { deleted: false, ...args };
return query(args);
},
// ... other query methods like findUnique, etc.
async delete({ model, args }) {
// translate "delete" to "update"
return (prisma as any)[model].update({
...args,
data: { deleted: true },
});
},
// ... deleteMany
},
},
});
تمام پرس و جوها و جهش ها با xprisma
مشتری اکنون رفتار حذف نرم دارد. مزیت اجرای حذف نرم با پسوندهای کلاینت این است که از آنجایی که پسوندهای کلاینت رفتار کلاینت اصلی پریسما را تغییر نمی دهند، همچنان می توانید از کلاینت اصلی برای واکشی همه موجودیت ها، از جمله مواردی که به عنوان حذف شده علامت گذاری شده اند، استفاده کنید.
یک خواننده کنجکاو ممکن است اجرای نمونه را ناقص بیابد. لطفا به ادامه مطلب بروید؛ ما بیشتر آن را در بخش Pitfalls پوشش خواهیم داد.
2. محدود کردن اندازه دسته نتیجه
پریسما findMany
متد به طور پیش فرض همه رکوردها را برمی گرداند، که می تواند یک رفتار ناخواسته برای جداول با ردیف های زیاد باشد. میتوانیم از برنامههای افزودنی مشتری برای افزودن محافظ ایمنی استفاده کنیم:
const MAX_ROWS = 100;
const xprisma = prisma.$extends({
name: 'max-rows',
query: {
$allModels: {
async findMany({ args, query }) {
return query({ ...args, take: args.take || MAX_ROWS });
},
},
},
});
3. ورود به سیستم
ورود به سیستم یکی دیگر از نگرانی های متقاطع بسیار رایج است. گاهی اوقات می خواهید برخی از عملیات مهم CRUD را ثبت کنید، اما فعال کردن ورود کامل در PrismaClient می تواند بسیار دشوار باشد. اکنون با برنامه های افزودنی مشتری به راحتی می توان به آن دست یافت.
const xprisma = prisma.$extends({
name: 'logging',
query: {
post: {
async delete({ args, query }) {
const found = await prisma.post.findUnique({
select: { title: true, published: true },
where: args.where,
});
if (found && found.published) {
myLogger.warn(`Deleting published post: ${found.title}`);
}
return query(args);
},
},
},
});
4. وضع قوانین کنترل دسترسی
اکثر برنامه های کاربردی مبتنی بر پایگاه داده دارای قوانین تجاری برای کنترل دسترسی هستند که باید به طور مداوم در مناطق مختلف ویژگی اعمال شوند. به طور سنتی عمل این است که آنها را در لایه API پیاده سازی کنید، اما مستعد ناسازگاری است. پسوندهای کلاینت Prisma اکنون امکان بیان آنها را نزدیکتر به پایگاه داده را ارائه می دهند.
فرض کنید در حال پیاده سازی API ها با Express.js هستید. شما می توانید این کار را به این صورت انجام دهید:
function getAuthorizedDb(prisma: PrismaClient, userId: number) {
return prisma.$extends({
name: 'authorize',
query: {
post: {
async findMany({ args, query }) {
return query({ ...args, where: { authorId: userId } });
},
// ... other operations
},
},
});
}
app.get('/posts', (req, res) => {
const userId = req.userId; // provided by some authentication middleware
return getPosts(getAuthorizedDb(userId));
});
زیبایی برنامههای افزودنی کلاینت در این است که موتور جستجو و استخر اتصال با کلاینت اصلی prisma که مبتنی بر آن است به اشتراک میگذارند، بنابراین هزینه ایجاد آنها بسیار کم است و میتوانید آن را در سطح درخواستی انجام دهید. همانطور که در کد بالا نشان داده شده است.
محدودیت ها و مشکلات
افزونه های مشتری هنوز نسبتاً جدید هستند و بدون محدودیت و دام نیستند. در اینجا چند مورد مهم وجود دارد که ممکن است بخواهید مراقب آنها باشید:
1. تایپ قوی همیشه جواب نمی دهد
Prisma کار بسیار خوبی انجام می دهد تا مطمئن شود که چیزها همیشه به خوبی تایپ می شوند. حتی برای برنامههای افزودنی مشتری، یکی از اهداف مهم طراحی، پشتیبانی از برنامهنویسی با تایپ قوی هنگام اجرای برنامههای افزودنی است. با این حال، همانطور که در مثال “حذف نرم” می بینید، در حال حاضر به طور مداوم قابل دستیابی نیست.
2. تمایل به پیاده سازی منطق تجاری با آنها
پسوندهای کلاینت به شما این امکان را می دهند که روش های دلخواه را به یک مدل یا کل کلاینت اضافه کنید. می تواند اجرای منطق تجاری با آن را برای شما وسوسه انگیز کند. به عنوان مثال، ممکن است بخواهید یک را اضافه کنید signUp
به مدل User، و علاوه بر ایجاد یک موجودیت، یک ایمیل فعال سازی نیز ارسال کنید. کار خواهد کرد، اما کد کسب و کار شما شروع به نفوذ به قلمرو پایگاه داده می کند و درک و عیب یابی پایه کد را سخت تر می کند.
با این حال، همانطور که قبلا نشان داده شد، نگرانی های متقاطع مانند حذف نرم، ورود به سیستم، کنترل دسترسی و غیره، موارد استفاده بسیار معتبری هستند.
3. تزریق شرایط فیلتر می تواند بسیار مشکل باشد
همانطور که در موارد استفاده شماره 1 و 4 مشاهده کردید، ما شرایط اضافی را به آرگ های پرس و جو Prisma تزریق کردیم تا به فیلتر اضافی برسیم. متأسفانه هیچکدام کاملاً صحیح نیستند. API query Prisma برای واکشی روابط بسیار منعطف است. بنابراین برای مثال “حذف نرم”، علاوه بر مدیریت سطح بالا findMany
، ما همچنین باید با واکشی رابطه سر و کار داشته باشیم، مانند:
prisma.user.findMany({ include: { posts: true } });
// should be injected as
prisma.user.findMany({
where: { deleted: false } },
include: { posts: { where: { deleted: false } }
});
، و اگر یک سلسله مراتب رابطه عمیق دارید باید به صورت بازگشتی پردازش شود. مراقب باشید که روش های جهش مانند update
، delete
از همین مشکل رنج می برند زیرا نتیجه آنها می تواند داده های رابطه را نیز با استفاده از عبارت include حمل کند. مثالی که استفاده کردیم یک سناریوی *-to- many است. پرداختن به رابطه به یک حتی سختتر است، زیرا واقعاً نمیتوانید فیلتری را روی واکشی طرف به یک رابطه وصل کنید. ایجاد یک پیاده سازی نشتی بسیار آسان است.
همه این پیچیدگیها ما را بر آن داشت تا جعبه ابزار ZenStack را برای بهبود سیستماتیک Prisma ایجاد کنیم و به شما این امکان را میدهد که نگرانیهای کنترل دسترسی را بهطور آشکار مدلسازی کنید. این جعبه ابزار در زمان اجرا کارهای سنگینی را انجام می دهد تا اطمینان حاصل شود که پرس و جوها به درستی فیلتر شده اند و جهش ها محافظت می شوند تا نیازی به پرداختن به همه ظرافت ها نداشته باشید.