Как загрузить запросы graphql с сервера, не определяя его во внешнем интерфейсе? - PullRequest
0 голосов
/ 02 ноября 2018

Теперь предположим, что мы используем REST API. У меня есть одна конечная точка, подобная этой: /homeNewsFeed. Этот API даст нам ответ, подобный следующему:

[
  {
    blockTitle: 'News',
    type: 'list',
    api: 'http://localhost/news'
  },
  {
    blockTitle: 'Photos',
    type: 'gallery',
    api: 'http://localhost/gallery'
  }
]

Теперь, получив это, мы проходим массив и вызываем соответствующие конечные точки для загрузки данных. У меня вопрос, как это сделать в GraphQL? Обычно мы определяем запрос в коде переднего конца. Без этого, как позволить серверу решить, что отправлять?

Основная причина сделать это. Представьте, что у нас есть мобильное приложение. Нам нужно добавить новые блоки в этот канал новостей, не отправляя обновление приложения. Но у каждого элемента может быть свой запрос.

1 Ответ

0 голосов
/ 08 ноября 2018

Обычно мы определяем запрос в коде переднего плана. Без этого, как позволить серверу решить, что отправлять?

Согласно spec запрос выполнения GraphQL должен включать две вещи: 1) схему; и 2) документ, содержащий определение операции. Определение операции определяет, какую операцию (какой запрос или мутацию) выполнить, а также формат ответа. Существуют обходные пути и исключения (я буду обсуждать некоторые из них ниже), но, в общем случае, , если указание формы ответа на стороне клиента нежелательно или как-то невозможно, следует внимательно рассмотреть вопрос о том, является ли GraphQL правильное решение для ваших нужд .

Кроме того, GraphQL предоставляет больше возможностей для одного запроса, а не для последовательности структурированных запросов, как требует существующий REST API. Таким образом, ответ будет выглядеть примерно так:

[
  {
    title: 'News',
    content: [
      ...
    ],
  },
  {
    title: 'Photos',
    content: [
      ...
    ],
  }
]

и соответствующий запрос может выглядеть так:

query HomePageContent {
  blocks {
    title
    content {
      # additional fields
    }
  }
}

Теперь возникает вопрос, как различать различные виды content. Обычно это решается путем использования интерфейса или объединения для объединения нескольких типов в один абстрактный тип. Точная структура вашей схемы будет зависеть от данных, которые вы отправляете, но вот пример:

interface BlockContentItem {
  id: ID!
  url: String!
}

type Story implements BlockContentItem {
  id: ID!
  url: String!
  author: String!
  title: String! 
}

type Image implement BlockContentItem {
  id: ID!
  url: String!
  alt: String!
}

type Block {
  title: String!
  content: [BlockContentItem!]!
}

type Query {
  blocks: [Block!]!
}

Теперь вы можете запросить blocks вот так:

query HomePageContent {
  blocks {
    title
    content {
      # these fields apply to all BlockContentItems
      __typename
      id
      url
      # then we use inline fragments to specify type-specific fields
      ... on Image {
        alt
      }
      ... on Story {
        author
        title
      }
    }
  }
}

Использование таких встроенных фрагментов гарантирует, что специфичные для типа поля возвращаются только для экземпляров этих типов. Я включил __typename, чтобы определить, к какому типу относится данный объект, что может быть полезно для клиентского приложения (такие клиенты, как Apollo, в любом случае автоматически включают это поле).

Конечно, все еще существует проблема того, что происходит, когда вы хотите добавить новый блок. Если содержимое блока соответствует существующему типу, нет проблем. Но что произойдет, когда вы ожидаете, что вам понадобится другой тип в будущем, но не можете создать дизайн вокруг этого прямо сейчас?

Как правило, такого рода изменения потребуют как изменения схемы на сервере, так и изменения запроса на клиенте. И в большинстве случаев это, вероятно, будет хорошо, потому что, если вы получаете данные в другой структуре, вам все равно придется обновить клиентское приложение . В противном случае ваше приложение не будет знать, как правильно отобразить новую структуру данных.

Но, допустим, мы все равно хотим проверить нашу схему на будущее. Вот два способа сделать это.

  1. Вместо указания интерфейса для content, просто используйте пользовательский скаляр JSON. Это фактически выбросит проверку ответа из окна, но позволит вам вернуть все, что вы хотите для содержимого данного блока.

  2. Абстрагируйте любые поля, которые могут понадобиться в будущем, в некоторый тип значения-ключа. Например:

.

type MetaItem {
  key: String!
  value: String!
}

type Block {
  title: String!
  meta: [MetaItem!]!
  # other common fields
}

Существует множество других обходных путей, некоторые лучше, чем другие, в зависимости от типа данных, с которыми вы работаете. Но, надеюсь, это даст вам некоторое представление о том, как обратиться к описанному вами сценарию в контексте GraphQL.

...