Search code examples
javascriptnode.jstypescriptprismanexus-prisma

get a nested email field that is part of another model in prisma?


i have schema that looks like:

schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

model User {
  id       String  @id @default(cuid())
  email    String? @unique
  stripeId String  @unique

  createdAt DateTime @default(now())

  product Product?

  @@map("users")
}

model Product {
  id       String @id @default(cuid())
  totalSum Int    @default(9700)

  user   User   @relation(fields: [userId], references: [id])
  userId String @unique

  licenses License[]

  @@map("products")
}

model License {
  id    String @id @default(cuid())
  name  String @unique
  /// no. of licenses generated
  total Int    @default(1)
  /// no. of licenses used
  used  Int    @default(0)
  /// stored in cents

  product   Product? @relation(fields: [productId], references: [id])
  productId String?

  createdAt DateTime @default(now())

  @@map("licenses")
}

i want to access email field while doing prisma.license.findMany(). my db file looks like:

db.ts

import { Prisma, License, User } from '@prisma/client'

import { prisma } from './context'

export const getLicenses = async (): Promise<
  Array<License & Pick<User, 'email'>> | null | undefined
> => {
  const userSelect = Prisma.validator<Prisma.ProductSelect>()({
    user: {
      select: {
        email: true,
      },
    },
  })

  const productSelect = Prisma.validator<Prisma.LicenseSelect>()({
    product: {
      include: userSelect,
    },
  })

  const licenses = await prisma.license.findMany({
    orderBy: {
      createdAt: 'desc',
    },
    include: productSelect,
  })

  const result = licenses.map((license) => {
    const email = license.product?.user.email

    if (email) {
      return {
        ...license,
        email,
      }
    }
  })

  return result
}

export const db = {
  getLicenses,
}

the last line return result gives this typescript error:

Type '({ email: string; id: string; name: string; total: number; used: number; productId: string | null; createdAt: Date; product: (Product & { user: { email: string | null; }; }) | null; } | undefined)[]' is not assignable to type '(License & Pick<User, "email">)[]'.
  Type '{ email: string; id: string; name: string; total: number; used: number; productId: string | null; createdAt: Date; product: (Product & { user: { email: string | null; }; }) | null; } | undefined' is not assignable to type 'License & Pick<User, "email">'.
    Type 'undefined' is not assignable to type 'License & Pick<User, "email">'.
      Type 'undefined' is not assignable to type 'License'.ts(2322)

my schema file looks like:

schema.ts

import { db } from './db'

const Query = objectType({
  name: 'Query',
  definition(t) {
    t.list.field('licenses', {
      type: 'License',
      resolve: async (_, __, ctx) => {
        if (!ctx.admin.isLoggedIn) return null
        const licenses = await db.getLicenses()

        if (licenses) return licenses
        return null
      },
    })
  },
})

even this little query is causing me a lot of errors. it used to work when i wanted to query all of licenses using prisma.license.findMany() but it started throwing errors as soon as i wanted email field but in a flat file format so my output looks like:

{
    id: string;
    name: string;
    total: number;
    used: number;
    productId: string | null;
    createdAt: Date;
    email: string;
}

i also don't want productId to be sent. how can i solve this?

i've made a minimal repro → https://github.com/deadcoder0904/prisma-nested-query-email


Solution

  • I disabled TS on the db.ts file & then checked results array & figured out I needed to have email as null instead of undefined.

    One important note: I had to stop using nonNull for email in nexus configuration of type License to stop the error.

    The working solution is posted in this commit. It works now :)