6

Context
I have a GraphQL API and a NodeJS & Angular application with a MongoDB database that holds users. For each user, there is a public page with public information like id and username. When a user is logged in, there is a private profile page with extended information like an email.

Just for context, I'm using jsonwebtoken with accesscontrol to authenticate and authorize a user. The information is stored on the Context of every GraphQL resolve function, so whatever is needed to identify a logged in user is available.

I have a GraphQL query that retrieves a public user like so:

query getUserById($id: ID!) {
  getUserById(id: $id) {
    id,
    username
  }
}

I am trying to think of the proper implementation to retrieve either a public or a private user. Since GraphQL is strong typed, I'm having some trouble coming up with a proper solution.

Question
How do I implement the distinction between a public and a private user?

Considerations

1. Separate query
So one of the options is to have a seperate query for both public and private fields:

public query

query getUserById($id: ID!) {
  getUserById(id: $id) {
    id,
    username
  }
}

private query

query getMe {
  getMe {
    id,
    username,
    email
  }
}

2. Using GraphQL Interfaces
I came across this Medium article that explains how GraphQL Interfaces are used to return different Types based on a resolveType function. So I would go something like so:

query getUser($id: ID!) {
   getUser(id: $id) {
     ... on UserPrivate {
       id,
       username
     }
     ... on UserPublic {
       id,
       username,
       email
     }
   }
}

I have not came across a proper solution and I'm unsure about either of the consideration I have so far.

Any help is much appreciated!

4

1 回答 1

9

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

type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
}

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

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

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

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

     # if you need a private field you can branch off here
     ... on UserPrivate {
       email
     }
   }
}

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

概括:

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

于 2018-03-07T13:10:39.323 回答