Prisma validator
The Prisma.validator
is a utility function that takes a generated type and returns a type-safe object which adheres to the generated types model fields.
This page introduces the Prisma.validator
and offers some motivations behind why you might choose to use it.
Note: If you have a use case for
Prisma.validator
, be sure to check out this blog post about improving your Prisma Client workflows with the new TypeScriptsatisfies
keyword. It's likely that you can solve your use case natively usingsatisfies
instead of usingPrisma.validator
.
Creating a typed query statement
Let's imagine that you created a new userEmail
object that you wanted to re-use in different queries throughout your application. It's typed and can be safely used in queries.
The below example asks Prisma
to return the email
of the user whose id
is 3, if no user exists it will return null
.
import { Prisma } from '@prisma/client'
const userEmail: Prisma.UserSelect = {
email: true,
}
// Run inside async function
const user = await prisma.user.findUnique({
where: {
id: 3,
},
select: userEmail,
})
This works well but there is a caveat to extracting query statements this way.
You'll notice that if you hover your mouse over userEmail
TypeScript won't infer the object's key or value (that is, email: true
).
The same applies if you use dot notation on userEmail
within the prisma.user.findUnique(...)
query, you will be able to access all of the properties available to a select
object.
If you are using this in one file that may be fine, but if you are going to export this object and use it in other queries, or if you are compiling an external library where you want to control how the user uses this object within their queries then this won't be type-safe.
The object userEmail
has been created to select only the user's email
, and yet it still gives access to all the other properties available. It is typed, but not type-safe.
Prisma
has a way to validate generated types to make sure they are type-safe, a utility function available on the namespace called validator
.
Using the Prisma.validator
The following example passes the UserSelect
generated type into the Prisma.validator
utility function and defines the expected return type in much the same way as the previous example.
import { Prisma } from '@prisma/client'
const userEmail: Prisma.UserSelect = {
email: true,
}
const userEmail = Prisma.validator<Prisma.UserSelect>()({
email: true,
})
// Run inside async function
const user = await prisma.user.findUnique({
where: {
id: 3,
},
select: userEmail,
})
Alternatively, you can use the following syntax that uses a "selector" pattern using an existing instance of Prisma Client:
import { Prisma } from '@prisma/client'
import prisma from './lib/prisma'
const userEmail = Prisma.validator(
prisma,
'user',
'findUnique',
'select'
)({
email: true,
})
The big difference is that the userEmail
object is now type-safe. If you hover your mouse over it TypeScript will tell you the object's key/value pair. If you use dot notation to access the object's properties you will only be able to access the email
property of the object.
This functionality is handy when combined with user defined input, like form data.
Combining Prisma.validator
with form input
The following example creates a type-safe function from the Prisma.validator
which can be used when interacting with user created data, such as form inputs.
Note: Form input is determined at runtime so can't be verified by only using TypeScript. Be sure to validate your form input through other means too (such as an external validation library) before passing that data through to your database.
import { Prisma, PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
// Create a new function and pass the parameters onto the validator
const createUserAndPost = (
name: string,
email: string,
postTitle: string,
profileBio: string
) => {
return Prisma.validator<Prisma.UserCreateInput>()({
name,
email,
posts: {
create: {
title: postTitle,
},
},
profile: {
create: {
bio: profileBio,
},
},
})
}
const findSpecificUser = (email: string) => {
return Prisma.validator<Prisma.UserWhereInput>()({
email,
})
}
// Create the user in the database based on form input
// Run inside async function
await prisma.user.create({
data: createUserAndPost(
'Rich',
'rich@boop.com',
'Life of Pie',
'Learning each day'
),
})
// Find the specific user based on form input
// Run inside async function
const oneUser = await prisma.user.findUnique({
where: findSpecificUser('rich@boop.com'),
})
The createUserAndPost
custom function is created using the Prisma.validator
and passed a generated type, UserCreateInput
. The Prisma.validator
validates the functions input because the types assigned to the parameters must match those the generated type expects.