Prismaのextendsを使って型安全にオブジェクトを拡張する
PrismaのExtensions機能を使用すると、PrismaClientを拡張することができます。 例えば、firstNameとlastNameを結合してfullNameを追加することができたり、クレデンシャルな情報を暗号化する処理を挟んだり、updateManyやdeleteManyを任意のクエリでのみ使用可能にすることができます。
firstNameとlastNameを結合してfullNameを追加する
prisma.$extends({
result: {
address: {
fullName: {
needs: { firstName: true, lastName: true },
compute({ firstName, lastName }) {
return `${firstName}${lastName}`;
},
},
},
},
}),
updateManyやdeleteManyを任意のクエリでのみ使用可能にする
type Models = keyof typeof Prisma.ModelName;
type Operation = "updateMany" | "deleteMany";
export type PrismaModelType<N extends Models = Models> = Prisma.TypeMap["model"][N];
export type DeleteManyArgs<N extends Models> = PrismaModelType<N>["operations"]["deleteMany"]["args"];
export type UpdateManyArgs<N extends Models> = PrismaModelType<N>["operations"]["updateMany"]["args"];
export type WhereInput<N extends Models = Models> = NonNullable<DeleteManyArgs<N>["where"]>;
export type UpdateInput<N extends Models = Models> = NonNullable<UpdateManyArgs<N>["data"]>;
type AllowedOperation<T extends Models> = {
model: T;
operation: Operation;
condition: (where: WhereInput<T>, data?: UpdateInput<T>) => boolean;
};
const allowedOperations: AllowedOperation<Models>[] = [
{
model: "PasswordResetToken",
operation: "deleteMany",
condition: (where) =>
"expiresAt" in where &&
typeof where.expiresAt === "object" &&
where.expiresAt !== null &&
"lt" in where.expiresAt &&
where.expiresAt.lt instanceof Date,
}
] as const;
prisma.$extends({
query: {
$allModels: {
async updateMany({ model, args, query }) {
const allowedOperation = allowedOperations.find(
(op) =>
op.model === model &&
op.operation === "updateMany" &&
args.where !== undefined &&
args.data !== undefined &&
op.condition(args.where, args.data),
);
if (!allowedOperation) {
throw new Error(`Unauthorized updateMany operation on ${model}`);
}
return query(args);
},
async deleteMany({ model, args, query }) {
const allowedOperation = allowedOperations.find(
(op) => op.model === model && op.operation === "deleteMany" && args.where !== undefined && op.condition(args.where),
);
if (!allowedOperation) {
throw new Error(`Unauthorized deleteMany operation on ${model}`);
}
return query(args);
},
},
},
}),