Архитектура
АрхитектураGraphQL-сервер в подходе code-first

GraphQL-сервер в подходе code-first

Схема GraphQL определяет контракты GraphQL-сервиса, предоставляя набор типов, полей и мутаций, которые могут выполняться против этого сервиса. При создании GraphQL-сервиса можно выбрать один из двух вариантов:

  • сделать схему единственным источником истины и привести весь код реализации в соответствие с её определениями
  • сделать код единственным источником истины и формировать схему как артефакт, генерируемый из кода

В обоих случаях мы получим полностью работающий GraphQL-сервис, однако в зависимости от выбранного подхода мы сможем реализовать больше или меньше возможностей, с большей или меньшей лёгкостью, в дальнейшем. Эти два подхода называются, соответственно, «schema-first» (более точное название — «SDL-first») и «code-first».

Gato GraphQL использует подход code-first. Давайте разберёмся, почему.

Почему Gato GraphQL использует code-first

В подходе code-first мы начинаем с написания резолверов, а затем на основе кода как единственного источника истины генерируем схему как артефакт. Таким образом, схема создаётся путём запуска скрипта, а не формируется вручную, как в SDL-first. Поскольку code-first тоже имеет схему, он не лишён ничего существенного из того, что предоставляет SDL-first.

Однако code-first предлагает важное преимущество перед SDL-first: возможность использовать динамические схемы, которые могут изменять свою форму и атрибуты в зависимости от контекста и управляться кодом во время выполнения. Действительно, все выдающиеся возможности Gato GraphQL являются прямым следствием его приверженности подходу code-first.

Преимущества code-first

Динамическая схема даёт, в числе прочего, все перечисленные ниже преимущества:

Источник истины для схемы является надмножеством того, что требует GraphQL. Дополнительные свойства (такие как глобальные поля, глобальные связи, глобальные директивы и сохранённые фрагменты) уже можно использовать в нашем API, не дожидаясь их добавления в спецификацию GraphQL — если оно вообще произойдёт.

Поскольку источник истины не привязан к схеме, мы можем генерировать любую схему и для любой другой системы: GraphQL — лишь один из возможных целевых форматов. Например, из того же источника истины можно сгенерировать JSON-схему для REST-сервиса.

API может быть одновременно публичным и приватным — в зависимости от того, вошёл ли пользователь в систему и каковы его роли, — или предоставлять больше или меньше полей в зависимости от какого-либо иного свойства, например, наличия у пользователя PRO-подписки.

Типы заранее не знают, какие поля им придётся разрешать. Вместо этого резолверы полей привязываются к резолверам типов посредством паттерна publish-subscribe, причём резолверы полей могут переопределять другие резолверы полей. Эта возможность делает API очень расширяемым: мы можем иметь общий код для нашего API и настраивать его на уровне приложения для конкретного клиента или проекта.

Поле может обрабатываться не одним, а несколькими резолверами: каждый резолвер поля в цепочке может решить во время выполнения, обрабатывать ли поле на основе какого-либо свойства или передать его дальше по цепочке. Например, специальный резолвер поля может использоваться только при передаче аргумента поля "source: testing", что позволяет тестировать его на нескольких сайтах в продакшене перед общим выпуском; та же стратегия позволяет оперативно устранять ошибки для конкретного клиента или окружения без риска непреднамеренных побочных эффектов в других местах.

Типы и интерфейсы могут автоматически получать пространства имён во избежание конфликтов со сторонними разработчиками.