Advanced type safety

The generated code for Prisma Client contains several helpful types and utilities that you can use to make your application more type-safe. This page describes patterns for leveraging them.

Note: If you're interested in advanced type safety topics with Prisma, be sure to check out this blog post about improving your Prisma workflows with the new TypeScript satisfies keyword.

Importing generated types

You can import the Prisma namespace and use dot notation to access types and utilities. The following example shows how to import the Prisma namespace and use it to access and use the Prisma.UserSelect generated type:

import { Prisma } from '@prisma/client'
// Build 'select' object
const userEmail: Prisma.UserSelect = {
email: true,
}
// Use select object
const createUser = await prisma.user.create({
data: {
email: 'bob@prisma.io',
},
select: userEmail,
})

See also: Using the Prisma.UserCreateInput generated type

What are generated types?

Generated types are TypeScript types that are derived from your models. You can use them to create typed objects that you pass into top-level methods like prisma.user.create(...) or prisma.user.update(...), or options such as select or include.

For example, select accepts an object of type UserSelect. Its object properties match those that are supported by select statements according to the model.

The first tab below shows the UserSelect generated type and how each property on the object has a type annotation. The second tab shows the resulting schema model.

Generated type
Model
type Prisma.UserSelect = {
id?: boolean | undefined;
email?: boolean | undefined;
name?: boolean | undefined;
posts?: boolean | Prisma.PostFindManyArgs | undefined;
profile?: boolean | Prisma.ProfileArgs | undefined;
}

In TypeScript the concept of type annotations is when you declare a variable and add a type annotation to describe the type of the variable. See the below example.

const myAge: number = 37
const myName: string = 'Rich'

Both of these variable declarations have been given a type annotation to specify what primitive type they are, number and string respectively. Most of the time this kind of annotation is not needed as TypeScript will infer the type of the variable based on how its initialized. In the above example myAge was initialized with a number so TypeScript guesses that it should be typed as a number.

Going back to the UserSelect type, if you were to use dot notation on the created object userEmail, you would have access to all of the fields on the User model that can be interacted with using a select statement.

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
profile Profile?
}
import { Prisma } from '@prisma/client'
const userEmail: Prisma.UserSelect = {
email: true,
}
// properties available on the typed object
userEmail.id
userEmail.email
userEmail.name
userEmail.posts
userEmail.profile

In the same mould, you can type an object with an include generated type then your object would have access to those properties on which you can use an include statement.

import { Prisma } from '@prisma/client'
const userPosts: Prisma.UserInclude = {
posts: true,
}
// properties available on the typed object
userPosts.posts
userPosts.profile

See the model query options reference for more information about the different types available.

Generated UncheckedInput types

The UncheckedInput types are a special set of generated types that allow you to perform some operations that Prisma Client considers "unsafe", like directly writing relation scalar fields. You can choose either the "safe" Input types or the "unsafe" UncheckedInput type when doing operations like create, update, or upsert.

For example, this Prisma schema has a one-to-many relation between User and Post:

model Post {
id Int @id @default(autoincrement())
title String @db.VarChar(255)
content String?
author User @relation(fields: [authorId], references: [id])
authorId Int
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}

The first tab shows the PostUncheckedCreateInput generated type. It contains the authorId property, which is a relation scalar field. The second tab shows an example query that uses the PostUncheckedCreateInput type. This query will result in an error if a user with an id of 1 does not exist.

Generated type
Example query
type PostUncheckedCreateInput = {
id?: number
title: string
content?: string | null
authorId: number
}

The same query can be rewritten using the "safer" PostCreateInput type. This type does not contain the authorId field but instead contains the author relation field.

Generated type
Example query
type PostCreateInput = {
title: string
content?: string | null
author: UserCreateNestedOneWithoutPostsInput
}
type UserCreateNestedOneWithoutPostsInput = {
create?: XOR<
UserCreateWithoutPostsInput,
UserUncheckedCreateWithoutPostsInput
>
connectOrCreate?: UserCreateOrConnectWithoutPostsInput
connect?: UserWhereUniqueInput
}

This query will also result in an error if an author with an id of 1 does not exist. In this case, Prisma Client will give a more descriptive error message. You can also use the connectOrCreate API to safely create a new user if one does not already exist with the given id.

We recommend using the "safe" Input types whenever possible.

Edit this page on GitHub