I have a problem, thx for any help.

With prisma we can use include with where conditions for models with a relation. If I make include conditions I get the right result. If I return it to the frontend it gets overwritten. I want to return exact my result from the backend.

I have at the frontend a query (ApolloClient, gql) like. It will return an array of comments for each post, I just want to have the first Comment for each post.

const POSTS = gql`
    query posts {
        posts(postId: $postId) {
            id
            comments{ // at the backend I have conditions for the comments
              id
            }
        }
    }
`;

Backend: Primsa and graphql nexus

Prisma Schema

model Post {
  id            String         @id @default(cuid())
  comments      Comment[]
}

model Comment {
  id            String         @id @default(cuid())
  post          Post           @relation(fields: [postId], references: [id])
  postId        String
}

Nexus Model

const Post = objectType({
  name: 'Post',
  definition(t) {
    t.model.id()
    t.model.comments()
})

const Comment = objectType({
  name: 'Comment',
  definition(t) {
    t.model.id()
    t.model.post()
    t.model.postId()
})

Resolver

export const posts = queryField('posts', {
  type: 'Post',
  list: true,
  args: {
    ...
  },
  resolve: async (_parent, args: any, { prisma, request }, info) => {
    
     const posts = await prisma.post.findMany({
      include: {
        comments: {
          take: 1
        }
      }
    })

    console.log(posts) 
    //Perfect result I want to return the include condition. But at the frontend I have all 
    //comments
    return posts
  },
})

The console.log(posts) is exact what I want to return!. Every post has an Array of ONE Comment. I return the posts and at the frontend every post has an Array of ALL Comments, what I don't want. How can I prevent that the frontend query overwrite the backend return? The fields are the same.

3

There are 3 answers

0
Ahmed Elywa On BEST ANSWER

I can't add a comment, so I am adding this to another answer.

Like I said with my PrismaSelect plugin, you can't use nexus-plugin-prisma t.model, t.crud. You will need to use Pal.Js CLI to autoGenerate all CRUD and ObjectTypes for all models.

const Post = objectType({
  name: 'Post',
  definition(t) {
    t.model.id()
    t.model.comments() // this field will overwritten by next one so this not needed
     t.list.field('comments', {
      type: 'Comment',
      list: true,
      resolve: (parent, args, { prisma }) => {
// here parent type include all other fields but not this field 
        return prisma.comment.findMany({ // this query is very wrong will case N+1 issue
          where: {
            postId: parent.id,
          },
          take: 1,
        })
      },
    })
})

Example

model User {
  id        Int       @default(autoincrement()) @id
  createdAt DateTime  @default(now())
  email     String    @unique
  name      String?
  password  String
  posts     Post[]
  comments  Comment[]
}

model Post {
  id        Int       @default(autoincrement()) @id
  published Boolean   @default(false)
  title     String
  author    User?     @relation(fields: [authorId], references: [id])
  authorId  Int?
  comments  Comment[]
}

model Comment {
  id        Int      @default(autoincrement()) @id
  contain   String
  post      Post     @relation(fields: [postId], references: [id])
  postId    Int
  author    User?    @relation(fields: [authorId], references: [id])
  authorId  Int?
}

Here is my Pal.Js CLI generated type for Post model

import { objectType } from '@nexus/schema'

export const Post = objectType({
  name: 'Post',
  definition(t) {
    t.int('id', { nullable: false })
    t.boolean('published', { nullable: false })
    t.string('title', { nullable: false })
    t.field('author', {
      nullable: true,
      type: 'User',
      resolve(parent: any) {
        return parent['author']
      },
    })
    t.int('authorId', { nullable: true })
    t.field('comments', {
      nullable: false,
      list: [true],
      type: 'Comment',
      args: {
        where: 'CommentWhereInput',
        orderBy: 'CommentOrderByInput',
        cursor: 'CommentWhereUniqueInput',
        take: 'Int',
        skip: 'Int',
        distinct: 'CommentDistinctFieldEnum',
      },
      resolve(parent: any) {
        return parent['comments']
      },
    })
  },
})

when you use my Pal.js CLI, your frontend request will be like this

query {
  findOnePost(where: {id: 1}) {
   comments(where: {}, take: 1){
    id
   }
  }
}
``
1
Ahmed Elywa On

The best way to handle this issue and just query what your frontend request to use my PrismaSelect plugin. Prisma Select takes the info: GraphQLResolveInfo object in general graphql arguments (parent, args, context, info) to select object accepted by prisma client. The approach allows a better performance since you will only be using one resolver to retrieve all your request. By doing so, it also eliminates the N + 1 issue.

Also, you can use my CLI to autogenerate all CRUD from your schema.prisma file https://paljs.com/generator/nexus

0
Tölz On

I mean I can add to my Post-ObjectType a field condition like:

const Post = objectType({
  name: 'Post',
  definition(t) {
    t.model.id()
    t.model.comments()
     t.list.field('comments', {
      type: 'Comment',
      list: true,
      resolve: (parent, args, { prisma }) => {
        return prisma.comment.findMany({
          where: {
            postId: parent.id,
          },
          take: 1,
        })
      },
    })
})

This is working. But if I understood it correct I have for every post one extra request. But I have already at the mutation resolver the right result. And I don't have the comments field at the parent (t.list.field- resolver)