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", что позволяет тестировать его на нескольких сайтах в продакшене перед общим выпуском; та же стратегия позволяет оперативно устранять ошибки для конкретного клиента или окружения без риска непреднамеренных побочных эффектов в других местах.
Типы и интерфейсы могут автоматически получать пространства имён во избежание конфликтов со сторонними разработчиками.