Apollo 和 Prisma Graphql 显式模型关系不可查询

Posted

技术标签:

【中文标题】Apollo 和 Prisma Graphql 显式模型关系不可查询【英文标题】:Apollo and Prisma Graphql explicit model relationship not queryable 【发布时间】:2021-10-14 07:02:26 【问题描述】:

我已经开始测试 prisma 2 和 graphql 以用于一个新的应用程序。我遇到了一个关于能够查询关系的显式多对多表的问题。

这是我的阿波罗模式:

scalar DateTime

type Query 
    user(id: String!): User
    users: [User]
    spaces: [Space]
    roles: [Role]


type Mutation 
    createUser(id: String!, email: String!): User!
    createSpace(name: String!): Space!


type User 
    id: ID!
    email: String!
    spaces: [UserSpace!]
    createdAt: DateTime!
    updatedAt: DateTime!


type Space 
    id: ID!
    name: String!
    users: [UserSpace!]
    createdAt: DateTime!
    updatedAt: DateTime!


type Role 
    id: ID!
    name: String!
    description: String!
    users: UserSpace
    createdAt: DateTime!
    updatedAt: DateTime!


type UserSpace 
    id: ID!
    user: User!
    space: Space!
    role: Role!
    createdAt: DateTime!
    updatedAt: DateTime!

这是我的棱镜架构:

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// npx prisma migrate dev
// npx prisma generate

datasource db 
  provider = "postgresql"
  url      = env("DATABASE_URL")


generator client 
  provider = "prisma-client-js"


model User 
  id String @id
  email String @unique
  spaces UserSpace[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt


model Space 
  id Int @default(autoincrement()) @id
  name String @unique
  users UserSpace[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt


model Role 
  id Int @default(autoincrement()) @id
  name String @unique
  description String
  users UserSpace[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt


model UserSpace 
  id Int @default(autoincrement()) @id
  user User @relation(fields: [userId], references: [id])
  userId String
  space Space @relation(fields: [spaceId], references: [id])
  spaceId Int
  role Role @relation(fields: [roleId], references: [id])
  roleId Int
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

这是我的突变解析器:

const  prisma  = require(".prisma/client");

async function createUser(parent, args, context, info) 
    return await context.prisma.user.create(
        data: 
            ...args,
        ,
    );


async function createSpace(parent, args, context, info) 
    const isAuthenticated = context.authentication.isAuthenticated;
    let role = null;

    if(!isAuthenticated) 
        throw new Error("Not Authenticated");
    

    try 
        role = await context.prisma.role.findUnique(
            where: 
                name: "Space Administrator",
            ,
        );
    
    catch(err) 
        throw new Error(err);
    

    return await context.prisma.space.create(
        data: 
            ...args,
            users: 
                create: 
                    role:  
                        connect:  id: role.id ,
                    ,
                    user: 
                        connect:  id: context.authentication.auth0Id ,
                    ,
                ,
            ,
        ,
    );


module.exports = 
    createUser,
    createSpace,

这是我的用户解析器(我知道这是问题所在,但我不知道如何解决问题):

function spaces(parent, args, context, info) 
    return context.prisma.user.findUnique( where:  id: parent.id  ).spaces();


module.exports = 
    spaces,

基本上,当我创建空间时,用户被添加为空间管理员,然后应该能够使用以下内容进行查询:

query 
  users 
    id
    email
    spaces 
      id
      role 
        name
      
      space 
        name
      
    
    createdAt
  

但是,当我运行查询时,出现以下错误:

"message": "Cannot return null for non-nullable field UserSpace.role.",

在 prisma 2 中,我如何使用户的解析器使用显式的多对多表以及它如何在其中具有第三个关系?我是 prisma 和 graphql 的新手,所以如果还有其他突出的内容,我也想提供意见。

【问题讨论】:

【参考方案1】:

您是否向用户 arg 提供了价值?像这样:users(id: "string_value")。因为是id 是必需的。

【讨论】:

是的 id 有一个值,但似乎用户解析器找不到正确的信息,因此它总是返回 null。我相信这是因为多对多具有第三种角色关系的性质。 我不明白你的评论?你是说用三个关系做一个关系表是不可能的? 我不是在说关于 Prisma 的任何事情,但是当您不提供所需的值时,这些显而易见的事情就会发生,因为它们都捆绑在一起。这就是我所说的 graphql 的工作原理。【参考方案2】:

我使用单词 type 来指代 GraphQL 架构中的对象模型,使用 model 来指代 Prisma 架构中的数据模型。

问题

我看到你有一个User 类型的解析器,它在你的User 类型中有一个User.spaces 字段的解析器函数。您在User.spaces 解析器中定义的查询将从数据库中返回相关的userSpace 记录。

但是,这些 userSpace 记录默认不解析 role 字段,因为它是一个关系字段。这就是 prisma 的工作原理(默认情况下不解析关系字段,除非明确说明)。

解决方案

UserSpace 类型创建解析器,并为UserSpace.role 字段显式定义解析器函数。这就是它的样子

// UserSpace resolver module

function role(parent, args, context, info) 
    return context.prisma.userSpace.findUnique( where:  id: parent.id  ).role();


module.exports = 
    role,


虽然还有其他一些方法可以解决这个问题,但推荐使用我展示的方法(以及特定的语法),因为在底层它允许 prisma 对solve the n+1 query problem 执行某些优化。但是,如果你不知道那是什么,你也不必担心。

【讨论】:

这更有意义,这正是我想要的。我希望 prisma 能够在明确的多对多关系上更新他们的文档,因为这些关系很可能在框架中更常见于自省。 澄清一下,这种行为并不局限于明确的多对多关系。默认情况下,查询期间不返回任何关系字段(和相应的对象)(包括隐式关系)。您可以通过使用includeselect 关键字并指定关系字段来更改此行为。但是,为这些关系字段中的每一个创建手动解析器(就像我展示的那样)是最佳实践,并且可以避免潜在的错误/极端情况问题。

以上是关于Apollo 和 Prisma Graphql 显式模型关系不可查询的主要内容,如果未能解决你的问题,请参考以下文章

使用 Apollo + GraphQL 将 Prisma 1 升级到 Prisma 2

Apollo 和 Prisma Graphql 显式模型关系不可查询

Prisma、GraphQL 和 Apollo 中具有突变的数据关系和连接类型

React-Apollo-Prisma-Graphql

Prisma v2:生成一个 .graphql 文件来编辑?

Apollo graphql:makeExecutableSchema 和游乐场