get full type on prisma client
Asked Answered
L

9

42

When i generate my prisma client with prisma generate, i got an index.d.ts with all types from my database. But the probleme is that all type are "single" and there is no relations. When i query something like

prisma.users.findMany({
            [... ]
            include: {
                cars: {
                  [...]
                }}});

prisma type the response dynamically with the right type

users & {
        cars: cars & {
           [...]
        };
    }

So everything work well and I have the auto completion, except if I want to pass this variable to another method, I would like to type the parameters, so I have to create my own type used as method parameter.

type prismaUsers = users & {
        cars?: cars & {
           [...]
        };
    }

But I'm asking if there is a way to get the "full" type from prisma to avoid me to create all "full" types with optionals sub-elements like I did for the user example. Or maybe i'm doing wrong and there is another way to do?

Lantha answered 13/7, 2021 at 16:21 Comment(0)
A
74

An alternate method (my preferrence) to get the "full" type, or the model with all of its possible relations, is to use the GetPayload version of your model type. The type is a generic that can receive the same object you pass to your query.

import { PrismaClient, Prisma } from "@prisma/client";

const prisma = new PrismaClient();

type UserWithCars = Prisma.UserGetPayload<{
  include: {
    cars: true;
  }
}>

const usersWithCars = await prisma.user.findMany({
  include: {
    cars: true,
  }
});

As your query grows more complex, you may want to abstract it and ensure that it is strongly typed.

import { Prisma, PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

const userInclude = Prisma.validator<Prisma.UserInclude>()({
  cars: true,
});

type UserWithCars = Prisma.UserGetPayload<{
  include: typeof userInclude;
}>;

const usersWithCars = await prisma.user.findMany({
  include: userInclude,
});

Amoeba answered 12/11, 2021 at 13:27 Comment(1)
Upvoting because your solution does not need to create a function just to retrieve a type :) Thanks!Irving
A
17

You can use the Prisma Validator API along with some typescript features to generate types for your query.

For the findMany example you mentioned

import { Prisma } from '@prisma/client'

// 1. Define a User type that includes the "cars" relation. 
const userWithCars = Prisma.validator<Prisma.UserArgs>()({
    include: { cars: true },
})

// 2: This type will include many users and all their cars
type UserWithCars = Prisma.UserGetPayload<typeof userWithCars>[]

If you simply want to automatically infer the return type of a prisma query wrapped in a function, you can use PromiseReturnType.

For example:

import { Prisma } from '@prisma/client'

async function getUsersWithCars() {
  const users = await prisma.user.findMany({ include: { cars: true } });
  return users;
}

type UsersWithCars = Prisma.PromiseReturnType<typeof getUsersWithCars>

You can read more about this in the Operating against partial structures of your model types concept guide in the Prisma docs.

Automat answered 14/7, 2021 at 12:7 Comment(0)
K
10

The best way is to use generic typing like this :

import { Prisma } from '@prisma/client'

type UserWithMessages = Prisma.UserGetPayload<{
  include: {
    Message: {
      include: {
        MessageParam: true;
      };
    };
  };
}>;
Kotz answered 17/1, 2022 at 15:52 Comment(0)
O
9

Full type of Model with all relations.

type UserFullType = Prisma.UserGetPayload<{ select: { [K in keyof Required<Prisma.UserSelect>]: true } }>

You could also create maps for convenience.

interface SelectMap {
  User: Prisma.UserSelect
  Post: Prisma.PostSelect
}

interface PayloadMap<S extends (string | number | symbol)> {
  User: Prisma.UserGetPayload<{ [K in S]: true }>
  Post: Prisma.PostGetPayload<{ [K in S]: true }>
}

type FullModel<M extends keyof SelectMap, S = Required<SelectMap[M]>> = PayloadMap<keyof S>[M]

Usage

const user: FullModel<'User'>

Update

Here is the workaround solution to make it using Model type as generic

type ValueOf<T> = T[keyof T]
type PickByValue<T, V extends T[keyof T]> = { [ K in Exclude<keyof T, ValueOf<{ [ P in keyof T ]: T[P] extends V ? never : P }>> ]: T[K] }
type KeyOfValue<T, V extends T[keyof T]> = keyof PickByValue<T, V>
type PickValueByKey<T, K> = K extends keyof T ? T[K] : never

interface ModelMap {
  Article: Article
  User: User
}
interface SelectMap {
  Article: Prisma.ArticleSelect
  User: Prisma.UserSelect
}
interface PayloadMap<S extends (string | number | symbol)> {
  Article: Prisma.ArticleGetPayload<{ select: { [K in S]: true } }>
  User: Prisma.UserGetPayload<{ select: { [K in S]: true } }>
}
type FullModelType<M extends ValueOf<ModelMap>, N = KeyOfValue<ModelMap, M>, S = Required<PickValueByKey<SelectMap, N>>> = PickValueByKey<PayloadMap<keyof S>, N>
const article: FullModelType<Article> = {}
Outlander answered 5/7, 2022 at 7:7 Comment(0)
U
3

Most easily you can define the corresponding type with typeof:

myQuery = await prisma.users.findMany({
            [... ]
            include: {
                cars: {
                  [...]
                }}});
type prismaUsers = typeof myQuery

Or if you decide to wrap your query inside a function, you may let TypeScript infer the return type for you:

function queryUserWithCar(...) {
  return prisma.users.findMany({
            [... ]
            include: {
                cars: {
                  [...]
                }}});
}

and then extract the function's return type:

type prismaUser = ReturnType<typeof queryUserWithCar> extends Promise<infer T> ? T : never
Unlearned answered 13/7, 2021 at 16:51 Comment(0)
C
3

The above example gives a really good solution, I just want to add how can you select specific attributes of a loaded entity.

In my example I have a todo entity and this entity has a author field on it. I want to load only name and id of the author entity

import { type Todo, Prisma } from "@prisma/client";

const todoInclude = Prisma.validator<Prisma.TodoInclude>()({
  author: {
    select: {
      name: true,
      id: true,
    },
  },
});

type TodoWithUser = Prisma.TodoGetPayload<{
  include: typeof todoInclude;
}>;
Coimbatore answered 23/6, 2023 at 15:14 Comment(0)
L
0

To clarify what I use this a year now since there was not a lot of answer yet:

I am still using my "own" type. I have a big file with all types where all properties are optional. It is not the best way because depending of the query, u have no access to all sub objects, but it is really pleasant to work with for the new devs.

It seem a bit heavy to develop but just need to add the new liaison when we change the database.

type prismaUser = users & {
        car?: prismaCar[];
        house?: prismaHouse;
    }

type prismaCar = car & {
        user?: prismaUser;
    }

type prismaHouse = house

On this example we only work with prismaUser, prismaCar and prismaHouse.

We think to change about new suggestions with generic type here when we gonna have time

Lantha answered 23/9, 2022 at 14:4 Comment(0)
S
0

Maybe this will help, I had this query

  await this.prisma.user.findMany({
    where: {
      patientData: {},
    },
    select: {
      id: true,
      firstName: true,
      lastName: true,
      email: true,
      patientData: {
        select: {
          credits: true,
        },
      },
      OrderHistory: {
        select: {
          name: true,
          date: true,
        },
      },
    },
  });

So I used RecursivePartial

export type RecursivePartial<T> = {
  [P in keyof T]?: RecursivePartial<T[P]>;
};
type UserWithPatientAndOrderHistory = RecursivePartial<
  Prisma.UserGetPayload<{
    include: {
      patientData: true;
      OrderHistory: true;
    };
  }>
>;
Stampede answered 1/5, 2023 at 14:38 Comment(0)
C
0

Here is an arbitrary solution that doesn't require you to do any extra work whenever you add a new model to your schema.

import {PrismaClient} from '@prisma/client';
import {EmptyObject} from 'type-fest';

/** All prisma model names. */
export type ModelName = keyof {
    [Model in keyof PrismaClient as PrismaClient[Model] extends {findFirstOrThrow: Function}
        ? Model
        : never]: boolean;
};

/** For a given model, extract all the available "include" properties and set them all to `true`. */
export type IncludeAll<Model extends ModelName> =
    NonNullable<NonNullable<Parameters<PrismaClient[Model]['findFirstOrThrow']>[0]>> extends {
        include?: infer IncludeArg;
    }
        ? Record<Exclude<keyof NonNullable<IncludeArg>, '_count'>, true>
        : EmptyObject;

export type BaseModel<Model extends ModelName> = NonNullable<
    Awaited<ReturnType<PrismaClient[Model]['findFirstOrThrow']>>
>;

export type JoinedModel<Model extends ModelName> = {
    [FieldName in Extract<keyof IncludeAll<Model>, string>]: Omit<
        ReturnType<PrismaClient[Model]['findFirstOrThrow']>,
        'then' | 'catch' | 'finally'
    > extends Record<FieldName, () => Promise<infer Result>>
        ? Result
        : `Error: failed to find relation for ${FieldName}`;
};

export type FullModel<Model extends ModelName> = JoinedModel<Model> & BaseModel<Model>;

After all of that, the following will work: FullModel<'users'>

Croze answered 17/6 at 23:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.