I'm kind of baffled when I see in the docs a descr...
# orm-help
p
I'm kind of baffled when I see in the docs a description of parameters like this:
XOR<UserSelect,null>.
That syntax means nothing to me. Is there some page that explains this syntax? I get from the example that somehow, it relates to where: {id: 1}... but I just don't see the connection and normally, I'm not the dumbest pencil in the box (so I had thought)
c
Is your confusion about Typescript types/syntax in general or specifically about the use of the
XOR
type? If the former, that is a bit of a longer explanation, but I'd be happy to try my best. If the later, that parameter is just saying that the
select
field must be either equal to
UserSelect
or to
null
(mutually exclusive). Technically
select
could also be
undefined
because of the
?
. In this particular case, I don't think use of
XOR
is strictly required (you could use the
|
instead), but there are other types like the
data?: XOR<UserUpdateInput, UserUncheckedUpdateInput>
field in your screenshot where the mutual exclusivity between the two options is important.
Also, here is the actual definition of the
XOR
type used in the index.d.ts file Prisma generates.
Copy code
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };

/**
   * XOR is needed to have a real mutually exclusive union type
   * <https://stackoverflow.com/questions/42123407/does-typescript-support-mutually-exclusive-types>
   */
  type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
p
The XOR makes sense now. I was thinking it somehow had to do with the select itself. I’m still lost on how a UserSelect type is formed. When I see an example like in the screenshot attached here, I get what it does but not how I would know what options are available to me.
c
The
UserSelect
type is determined by all of the fields you can select on the User model based on your Prisma schema. So if you defined the following model in your schema.prisma file:
Copy code
model User {
  id Int @id @default(autoincrement())
  name String
  email String
  nickname String?
}
The UserSelect type generated would look like:
Copy code
type UserSelect = {
  id?: boolean
  name?: boolean
  email?: boolean
  nickname?: boolean
}
If you did some like the following:
Copy code
const user = await prisma.user.findMany({
  select: {
    id: true
  }
})
You would be fetching all Users in your database, but only selecting the id field on those users. So the resulting array would look like:
Copy code
[{id: 1}, {id: 2}, ...]
To determine what options are available, I use the combination of my knowledge of what fields are on a particular model, my IDE's autocomplete, plus (in VS Code) you can right click and go to the type definition to be certain.
The
include
functionality operates similar to
select
, except that it returns all of the scalar fields plus any of the nested relations you want to fetch. So in the second screenshot, the returned Users would have all of the scalar fields defined in the Prisma schema for a User + all of the Posts related to each User (with all of the Posts' scalar fields) + all of the Categories related to each of those Posts.
p
Ok. Thanks. I’ll experiment
c
You could do something like:
Copy code
const user = await prisma.user.findMany({
  include: {
     organization: {
        select: {
          name: true
        }
     }
  }
})
To get all Users and all scalar fields on those Users + each User's organization, but only include the name of the organization (don't get any other fields of the organization). In the absence of any
include
or
select
, Prisma will return all of the scalar fields on the model, but none of the nested relation's fields. It will however return the scalar field used to reference the relation (e.g. on User it might be an integer called
organizationId
).
p
I’m still confused, not on what it does, but how the syntax is defined. That is, the include and select keywords. Is there a syntax tree someplace that makes that clear for what is allowed?
c
I think the best way to see that would be to setup a basic project with a couple models + relations. Generate the Prisma client and then look at the generated type file. The type file should live at
node_modules/.prisma/client/index.d.ts
once you generate it. This file can get pretty large, but should be manageable to look through for a small project. I'd probably start by finding the type definition for the args accepted by a
findUnique
or
findMany
for one of your models. You can then poke around there. Alternatively, if you have an editor with good autocomplete you could just start trying to write valid Prisma queries. I'm not sure if there is a formal definition of the args everything accepts in the docs, but there is a consistent pattern that you can identify. I picked it up as I went and everything tended make sense once I got the hang of it. For example, a
findUnique
will always accept:
where
,
select
,
includes
, and
rejectOnNotFound
The
where
on a
findUnique
will only accept any of the unique fields on your model. The
select
will accept any of the scalar fields on the model or any nested relations. For scalars it will expect a boolean. For nested one to one relations it will accept:
select
,
include
. For nested one to many relations it will accept:
select
,
include
,
where
,
orderBy
, etc. (all of the fields a top-level
findMany
would accept. The
include
would accepted any of the nested relations. I found that I just started using Prisma and with a little bit of consulting the docs as I needed to use different parts + a lot of autocomplete and jumping to the type definition for my specific models I figured it out as I went.
💯 1