首页 > 解决方案 > 使用 Prisma 生成的包装器查询节点时包含关系

问题描述

我正在关注 Prisma 提供的GraphQL Prisma Typescript示例,并创建了一个简单的数据模型,为 Prisma 客户端和解析器等生成了代码。

我的数据模型包括以下节点:

type User {
  id: ID! @unique
  displayName: String!
}

type SystemUserLogin {
  id: ID! @unique
  username: String! @unique
  passwordEnvironmentVariable: String!
  user: User!
}

我已经播种了系统用户和用户。

mutation {
  systemUserLogin: createSystemUserLogin({
    data: {
      username: "SYSTEM",
      passwordEnvironmentVariable: "SYSTEM_PASSWORD",
      user: {
        create: {
          displayName: "System User"
        }
      }
    }
  })
}

我创建了一个示例突变login

login: async (_parent, { username, password }, ctx) => {
    let user
    const systemUser = await ctx.db.systemUserLogin({ username })
    const valid = systemUser && systemUser.passwordEnvironmentVariable && process.env[systemUser.passwordEnvironmentVariable] &&(process.env[systemUser.passwordEnvironmentVariable] === password)

    if (valid) {
      user = systemUser.user // this is always undefined!
    }

    if (!valid || !user) {
      throw new Error('Invalid Credentials')
    }

    const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET)

    return {
      token,
      user: ctx.db.user({ id: user.id }),
    }
  },

但无论我做什么,systemUser.user总是未定义!

这是有道理的 - 客户端包装器如何知道在没有我告诉它的情况下递归到图中的“深度”?

但是我怎么能告诉它我想要包含这个User关系呢?

编辑:我尝试了下面的建议来使用prisma-client.

但是我的解析器似乎都没有被调用...

export const SystemUserLogin: SystemUserLoginResolvers.Type<TypeMap> = {
  id: parent => parent.id,
  user: (parent, args, ctx: any) => {
    console.log('resolving')
    return ctx.db.systemUserLogin({id: parent.id}).user()
  },
  environmentVariable: parent => parent.environmentVariable,
  systemUsername: parent => parent.systemUsername,
  createdAt: parent => parent.createdAt,
  updatedAt: parent => parent.updatedAt
};

和...

  let identity: UserParent;

  const systemUserLogins = await context.db.systemUserLogins({
    where: {
      systemUsername: user,
    }
  });
  const systemUserLogin = (systemUserLogins) ? systemUserLogins[0] : null ;

  if (systemUserLogin && systemUserLogin.environmentVariable && process.env[systemUserLogin.environmentVariable] && process.env[systemUserLogin.environmentVariable] === password) {
    console.log('should login!')

    identity = systemUserLogin.user; // still null
  }

编辑2:这是存储库

https://github.com/jshin47/annotorious/tree/master/server

标签: graphqlprismaplumatic-schemaprisma-graphql

解决方案


目前有两种方法可以解决这个问题:

  • 目前像 OP 一样使用Prisma 客户端
  • 按照@User97 在接受的答案中的建议使用Prisma 绑定

您可以在此论坛帖子中了解更多关于 Prisma 客户端和 Prisma 绑定之间的区别。

由于 OP 目前正在使用 Prisma 客户端,因此我也将其用于此答案!

让我们看一下OP在问题中所做的声明:

这是有道理的 - 客户端包装器如何知道在没有我告诉它的情况下递归到图中的“深度”?

OP 正确地指出,Prisma 客户端不知道如何深入到图表中以及要获取哪些关系。事实上,除非明确告知(例如使用$fragmentAPI),客户端永远不会获取任何关系,并且总是只会获取标量值。来自Prisma 文档

每当使用 Prisma 客户端查询模型时,都会获取该模型的所有标量字段。无论是查询单个对象还是对象列表,都是如此。

那么,如何正确解决这种情况呢?事实上,解决方案不是改变 Prisma 客户端的使用方式,而是实现一个额外的 GraphQL 解析器功能!

解析器的关键在于它们正在获取 架构中特定字段的数据。在 OP 的情况下,目前没有解析器可以“解析”user在类型上定义的关系SystemUserLogin

type SystemUserLogin {
  id: ID! @unique
  username: String! @unique
  passwordEnvironmentVariable: String!
  user: User! # GraphQL doesn't know how to resolve this
}

要解决这种情况,您需要为它实现一个专用的“类型解析器”,如下所示:

const resolvers = {
  SystemUserLogin: {
    user(parent, args, ctx) {
      return ctx.db.systemUserLogin({id: parent.id}).user()
    }
  } 
}

全面披露:我在 Prisma 工作,我们正在努力为该用例添加更好的文档和资源。另请查看示例,其中出于相同的原因需要显式解析器author和关系字段。posts

希望有帮助!

编辑:我们还在 Prisma 教程中添加了关于通用解析器模式的更彻底的解释。


推荐阅读