Prisma 和 ApolloClient:防止前端为关系覆盖后端的包含条件

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

我有一个问题,感谢您的帮助。

有了 prisma,我们可以为具有关系的模型使用 include with where 条件。如果我包含条件,我会得到正确的结果。如果我 return 它到前端它会被覆盖。我想 return 从后端精确我的结果。

我在前端有一个查询(ApolloClient,gql)之类的。它将 return 每个 post 的评论数组,我只想为每个 post 获得第一个评论。

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

后端:Primsa 和 graphql nexus

Prisma 模式

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
}

连结模型

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()
})

解析器

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
  },
})

console.log(posts) 正是我想要的 return!。每个 post 都有一个评论数组。 我 return post 并且在前端每个 post 都有一个包含所有评论的数组,这是我不想要的。如何防止前端查询覆盖后端 return?字段相同。

我的意思是我可以在我的 Post-ObjectType 中添加一个字段条件,例如:

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,
        })
      },
    })
})

这是有效的。但是,如果我理解正确的话,我对每个 post 都有一个额外的请求。但是我已经在突变解析器中得到了正确的结果。而且我在父级(t.list.field- 解析器)

中没有评论字段

处理此问题的最佳方法是查询您的前端请求使用我的 PrismaSelect 插件。 Prisma Select 将一般 graphql 参数(parent、args、context、info)中的 info: GraphQLResolveInfo 对象带到 prisma client 接受的 select 对象。该方法允许更好的性能,因为您将只使用一个解析器来检索您的所有请求。通过这样做,它也消除了 N + 1 问题。

此外,您可以使用我的 CLI 从您的 schema.prisma 文件自动生成所有 CRUD https://paljs.com/generator/nexus

我无法添加评论,所以我将其添加到另一个答案中。

正如我在 PrismaSelect 插件中所说的,您不能使用 nexus-plugin-prisma t.modelt.crud。您将需要使用 Pal.Js CLI 为所有模型自动生成所有 CRUD 和对象类型。

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,
        })
      },
    })
})

例子

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?
}

这是我的 Pal.Js CLI 为 Post 模型生成的类型

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']
      },
    })
  },
})

当你使用我的 Pal.js CLI 时,你的前端请求将是这样的

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