Skip to main content

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 TypeScript satisfies keyword. It's likely that you can solve your use case natively using satisfies instead of using Prisma.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.