GraphQL - 如何区分公共和私有字段?

Nic*_*cky 6 access-control node.js role-based-access-control graphql graphql-js

上下文
我有一个 GraphQL API 和一个 NodeJS & Angular 应用程序,其中包含一个包含用户的 MongoDB 数据库。对于每个用户,都有一个包含公共信息的公共页面,例如idusername。当用户登录时,会有一个包含扩展信息的私人资料页面,例如email.

仅就上下文而言,我使用jsonwebtokenaccesscontrol来对用户进行身份验证和授权。该信息存储在每个 GraphQL 解析函数的上下文中,因此可以使用识别登录用户所需的任何信息。

我有一个 GraphQL 查询,它检索公共用户,如下所示:

query getUserById($id: ID!) {
  getUserById(id: $id) {
    id,
    username
  }
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试考虑检索公共用户或私人用户的正确实现。由于 GraphQL 是强类型的,我在想出合适的解决方案时遇到了一些麻烦。

问题
如何区分公共用户和私人用户?

注意事项

1. 单独查询
因此,选项之一是对公共和私有字段进行单独查询:

公开查询

query getUserById($id: ID!) {
  getUserById(id: $id) {
    id,
    username
  }
}
Run Code Online (Sandbox Code Playgroud)

私人查询

query getMe {
  getMe {
    id,
    username,
    email
  }
}
Run Code Online (Sandbox Code Playgroud)

2. 使用 GraphQL 接口
我看到了这篇 Medium 文章,它解释了如何使用 GraphQL 接口根据resolveType函数返回不同的类型。所以我会这样做:

query getUser($id: ID!) {
   getUser(id: $id) {
     ... on UserPrivate {
       id,
       username
     }
     ... on UserPublic {
       id,
       username,
       email
     }
   }
}
Run Code Online (Sandbox Code Playgroud)

我还没有遇到合适的解决方案,我不确定到目前为止我所考虑的任何一个。

任何帮助深表感谢!

Her*_*rku 8

我认为您在这里缺少的是在 GraphQL 中您通常想要创建这种深度连接的图结构。虽然getUserByIdgetMe作为入口点工作得很好(我认为即使使用接口类型它们仍然是一个好主意),您很可能会在您的架构中出现用户类型。想象一下流行的博客文章示例:

type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
}
Run Code Online (Sandbox Code Playgroud)

在这里添加两个作者字段并不能很好地工作。同样,在您的示例中,您可能不知道个人资料页面是您自己的,直到您收到后端的响应(想想推特个人资料)。

相反,我认为有两种方法可以考虑:

第一个是界面理念。您将拥有一个接口,其中包含私有和公共类型的所有公共字段和具体实现。这里的好处是:如果您只使用公共字段,您甚至不必使用类型匹配:

query getUser($id: ID!) {
   getUser(id: $id) {
     id
     username

     # if you need a private field you can branch off here
     ... on UserPrivate {
       email
     }
   }
}
Run Code Online (Sandbox Code Playgroud)

当它变得更细粒度(人们分享他们想要向公众公开的内容,想象一下 Facebook)或者您有很多类型(UserMe、UserFriend、UserStranger)时,您可能需要考虑可空字段。如果您无权访问该字段,您将收到来自 API 的 null。为了减少空检查的数量,您可以轻松地将字段捆绑到它们自己的类型中(例如Address)。

概括:

从 API 的角度来看,返回可空字段要容易一些,因为它为您提供了很大的灵活性。与第一个选项相比,在不破坏更改的情况下演化第二个选项要容易得多。如果您使用静态类型(Typescript、Flow、Scala.js、Reason 等),那么在前端使用接口会更具表现力,而且肯定会更有趣。关键字:模式匹配。