GraphQL - Как отличить открытые поля от закрытых?

контекст
У меня есть API-интерфейс GraphQL и приложение NodeJS & Angular с базой данных MongoDB, которая содержит пользователей. Для каждого пользователя есть общедоступная страница с общедоступной информацией, такой как id а также username, Когда пользователь входит в систему, появляется личная страница профиля с расширенной информацией, такой как email,

Просто для контекста, я использую jsonwebtoken с accesscontrol для аутентификации и авторизации пользователя. Информация хранится в контексте каждой функции разрешения GraphQL, поэтому доступно все, что необходимо для идентификации вошедшего в систему пользователя.

У меня есть запрос GraphQL, который получает публичного пользователя, например, так:

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

Я пытаюсь придумать правильную реализацию для извлечения публичного или частного пользователя. Так как GraphQL строго типизирован, у меня возникли проблемы с поиском правильного решения.

Вопрос
Как мне реализовать различие между публичным и частным пользователем?

Соображения

1. Отдельный запрос
Таким образом, один из вариантов состоит в том, чтобы иметь отдельный запрос для открытых и закрытых полей:

публичный запрос

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

частный запрос

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

2. Использование интерфейсов GraphQL
Я наткнулся на эту статью среднего уровня, которая объясняет, как интерфейсы GraphQL используются для возврата различных типов на основе resolveType функция. Так что я бы пошел что-то вроде этого:

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

Я не нашел правильного решения, и я не уверен ни в одном из соображений, которые у меня есть до сих пор.

Любая помощь высоко ценится!

1 ответ

Решение

Я думаю, что вам здесь не хватает того, что в GraphQL вы обычно хотите создать эту глубоко связанную структуру графа. В то время как getUserByIdа также getMe хорошо работают как точки входа (и я думаю, что они все еще хорошая идея даже с типом интерфейса), вы, скорее всего, будете иметь типы пользователей, появляющиеся по всей вашей схеме. Представьте себе пример популярного поста в блоге:

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. Чтобы уменьшить количество нулевых проверок, вы можете легко связать поля в их собственные типы (например, Address).

Резюме:

С точки зрения API немного проще возвращать пустые поля, потому что это дает вам большую гибкость. Гораздо проще развить второй вариант, не нарушая изменений, чем первый. Использование интерфейсов является более выразительным и, безусловно, более интересным для работы во внешнем интерфейсе, если вы работаете со статическими типами (Typescript, Flow, Scala.js, Reason и т. Д.). Ключевое слово: сопоставление с образцом.

Другие вопросы по тегам