В настоящее время я пишу сервер POC GraphQL. Однако пользовательскому интерфейсу клиента необходимы временные поля (не являющиеся частью модели БД), которые формируются или запрашиваются на лету. Например: Post может понравиться, поэтому я хотел бы поставить флаг isLiked: Boolean в graphQL. В зависимости от вызывающего абонента этот флаг будет true, если Post нравится, или false, если нет.
Однако это кажется неправильным, поскольку, строго говоря, это не часть Post-type и является формой UI-сопряжения (то, что мы хотели решить с помощью GraphQL). Также у меня есть ощущение, что может быть лучший способ предоставить Like в качестве типа (например, у него также есть date). Было бы неплохо, чтобы поля, зависимые от вызывающего абонента, были определены в типе, которые в основном являются подзапросами?


У GraphQL есть некоторые ограничения, и я не считаю неправильным добавлять в схему поля особого случая, чтобы обойти эти ограничения, особенно если они представляют собой некоторый запрос, который будет выполнять какое-то реальное приложение (и часто).
И наоборот, поскольку вызывающий объект всегда указывает, какие именно поля он хочет получить от объекта, добавление дополнительных полей не должно вам ничего стоить с точки зрения производительности или запросов к базе данных. Итак: делайте и то, и другое!
scalar DateTime
interface Node { id: ID! }
type Like implements Node {
id: ID!
post: Post!
date: DateTime!
}
type Post implements Node {
id: ID!
title: String!
date: DateTime!
likes: [Like!]!
hasLike: Bool!
}
Если вы этого не сделаете, то единственный выбор клиента - запросить определенные похожие объекты и выбрать из них какое-то поле. Если вы добавите в поле параметры типа ограничения, вы сможете минимизировать затраты, но это все равно будет немного неудобно.
query PostSummary($id: ID!) {
node(id: $id) {
... on Post {
title
date
likes(limit: 1) { id }
}
}
}
Если это реальный вариант использования вашего приложения, простое добавление поля hasLike кажется более разумным API, даже если оно в некоторой степени «специализировано для пользовательского интерфейса».
Я согласен с ответом Дэвида, но просто чтобы предложить другую точку зрения, есть кое-что, что нужно сказать о сохранении пользовательских полей из типов, которые в противном случае не являются. Доступна еще одна альтернатива - переместить такие поля в запрос пользователя или средства просмотра, который уже возвращает данные, специфичные для вошедшего в систему пользователя. Например, у вас может быть
type User {
id: ID!
username: String!
likedPosts: [Post!]!
# or better yet
likedPostIds: [ID!]!
}
Конечно, недостатком этого подхода является то, что ваш клиент должен быть достаточно «умным», чтобы использовать вышеизложенное, чтобы затем определить, понравился ли пост, что добавляет сложности во внешнем интерфейсе.
Положительным моментом является то, что если вы выполняете выход из системы или переключаете пользователей, вам нужно только повторно получить один запрос - вам не нужно удалять весь кеш, потому что он приправлен пользовательскими данными, которые теперь придется обновлять.
Такой подход также может улучшить производительность. Любое реляционное поле, независимо от того, является ли он конкретным для пользователя или нет, требует дополнительных затрат. При таком подходе ваш запрос на вход может быть раздутым и медленным, но любые последующие запросы будут намного быстрее. По мере роста ваших данных, как с точки зрения широты, так и глубины, этот прирост производительности может быть значительным.