Selections
Includes on types
In some cases, you may want to always pre-load certain relations. This can be helpful for defining fields directly on type where the underlying data may come from a related table.
builder.prismaObject('User', {
// This will always include the profile when a user object is loaded. Deeply nested relations can
// also be included this way.
include: {
profile: true,
},
fields: (t) => ({
id: t.exposeID('id'),
email: t.exposeString('email'),
bio: t.string({
// The profile relation is nullable, so this field is too.
nullable: true,
// The profile relation will always be loaded, and user will now be typed to include the
// profile field so you can return the bio from the nested profile relation.
resolve: (user) => user.profile?.bio,
}),
}),
});Select mode for types
By default, the prisma plugin will use include when including relations, or generating fallback
queries. This means we are always loading all columns of a table when loading it in a
t.prismaField or a t.relation. This is usually what we want, but in some cases, you may want to
select specific columns instead. This can be useful if you have tables with either a very large
number of columns, or specific columns with large payloads you want to avoid loading.
To do this, you can add a select instead of an include to your prismaObject:
builder.prismaObject('User', {
select: {
id: true,
},
fields: (t) => ({
id: t.exposeID('id'),
email: t.exposeString('email'),
}),
});The t.expose* and t.relation methods will all automatically add selections for the exposed
fields WHEN THEY ARE QUERIED, ensuring that only the requested columns will be loaded from the
database.
In addition to the t.expose and t.relation, you can also add custom selections to other fields:
builder.prismaObject('User', {
select: {
id: true,
},
fields: (t) => ({
id: t.exposeID('id'),
email: t.exposeString('email'),
bio: t.string({
// This will select user.profile.bio when the `bio` field is queried
select: {
profile: {
select: {
bio: true,
},
},
},
resolve: (user) => user.profile.bio,
}),
}),
});A field-level select always adds to the row of the model the field is defined on, whatever type
the field returns. A select on a t.prismaField defined on User adds columns to the user its
resolver receives as the parent, not to the model the field returns.
Using arguments or context in your selections
The following is a slightly contrived example, but shows how arguments can be used when creating a selection for a field:
const PostDraft = builder.prismaObject('Post', {
fields: (t) => ({
title: t.exposeString('title'),
commentFromDate: t.string({
args: {
date: t.arg({ type: 'Date', required: true }),
},
select: (args) => ({
comments: {
take: 1,
where: {
createdAt: {
gt: args.date,
},
},
},
}),
resolve: (post) => post.comments[0]?.content,
}),
}),
});