Search code examples
typescriptprismatyping

Prisma Typescript typing when using select to not return the entire object


The good thing about using Prisma is that typesafety is super siple to apply.

Let's go with an example, that I have a user model like the following:

model User {
    id                String      @id @default(cuid())
    name              String
    email             String?     @unique
    emailVerified     DateTime?
    image             String?
}

When I now query it and put it for example into a state like:

import { type User } from "@prisma/client";
const [user, setUser] = useState<User | null>(null);
return await prisma.user.findUnique({
   where: {
      id: userId,
   }
});

Then the typing would be perfectly fine. Now the problem:

In case I do not want people to be able to query all the information given on a user, I would limit the return by setting a select like this:

return await prisma.user.findUnique({
   where: {
      id: userId,
   },
   select: {
      id: true,
      name: true,
   }
});

The problem now. Typescript now changes the return object to something super boring like

{
   id: string,
   name: string,
}

So then I's need to change the state for example to this:

import { type User } from "@prisma/client";
const [user, setUser] = useState<{
   id: string,
   name: string,
}| null>(null);

And when you add multiple complex includes it just get super messy. Am I missing something obvious on how to limit those types on the frontend?

Cheers!


Solution

  • You're not returning a user. You're returning a subset of a user.

    So you can make a type that matches yourself with:

    Pick<User, 'name' | 'id'>
    

    Or:

    Omit<User, 'emailVerfied' | 'image' | 'passwordHash'>
    

    And if you use that a lot you can just make your own type to import wherever you want:

    // somefile.ts
    import { type User } from "@prisma/client";
    export type PublicUser = Pick<User, 'name' | 'id'>
    
    
    // my-component.tsx
    import { type PublicUser } from './somefile'
    
    const [user, setUser] = useState<PublicUser | null>(null);