Exercise 2: displayName and findByEmail — Possible Solution ============================================================= export const userHelpers = Prisma.defineExtension((client) => client.$extends({ name: "userHelpers", result: { user: { displayName: { needs: { name: true, email: true }, compute(user) { const name = user.name?.trim(); return name ? name : user.email.split("@")[0]; }, }, }, }, model: { user: { async findByEmail(email: string) { return client.user.findFirst({ where: { email: { equals: email.trim(), mode: "insensitive" } }, }); }, }, }, }), ); const prisma = base.$extends(userHelpers); const u = await prisma.user.findByEmail(" ADA@Example.com "); console.log(u?.displayName); // "Ada", or "ada" if she has no name Notes: - mode: "insensitive" works on PostgreSQL and MongoDB. On MySQL, case sensitivity depends on the column's collation; SQLite needs a different approach (for example storing emails in lower case). - findFirst is used rather than findUnique, because findUnique only accepts exact matches on unique fields. - A better long-term fix is to store emails lower-cased when users sign up, so a plain findUnique works everywhere. WHY THIS WORKS AS AN ANSWER ------------------------------ displayName lists both fields it depends on in needs, so it works even when the caller only selects displayName. The defineExtension(client => client.$extends(...)) form gives the model method access to the client it's attached to, so it doesn't depend on a separate global. The notes are honest about where case-insensitive matching is and isn't supported.