【问题标题】:Apollo and Prisma Graphql explicit model relationship not queryableApollo 和 Prisma Graphql 显式模型关系不可查询
【发布时间】: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 的新手,所以如果还有其他突出的内容,我也想提供意见。

【问题讨论】:

    标签: graphql apollo prisma resolver prisma2


    【解决方案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 关键字并指定关系字段来更改此行为。但是,为这些关系字段中的每一个创建手动解析器(就像我展示的那样)是最佳实践,并且可以避免潜在的错误/极端情况问题。
    猜你喜欢
    • 2020-07-02
    • 2018-10-19
    • 2019-09-03
    • 2017-03-26
    • 2019-12-19
    • 2019-08-05
    • 2019-09-01
    • 2021-10-03
    • 2020-04-14
    相关资源
    最近更新 更多