Search code examples
apollo-clientprismanexus-prismaprisma2

Prisma and ApolloClient: Prevent overwriting the include conditions at the backend by the frontend for relations


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.


Solution

  • 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
       }
      }
    }
    ``