Основы GraphQL

| June 9, 2022

Эта статья содержит введение в GraphQL и её концепции. Если Вы хотите начать изучать GraphQL, но не уверены с чего начать, эта статья станет прекрасным введением в технологию для Вас.

Что такое GraphQL?

“GraphQL это язык запросов для API-ев и рантайм для выполнения этих запросов с Вашими данными”

Сказано на сайте проекта. Но что в действительности означают эти слова?

А означают они, что GraphQL определяет собственный Domain Specific Language (DSL) с помощью которого клиенты получают доступ к данным. Они так же означают, что GraphQL определяет инструменты, которые Вы можете расширить для того, чтобы организовать доступ к данным, применимым для Вашего конкретного случая.

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

Концепции

GraphQL определяет 4 основных концепции.

Схема

Если Вы хотя бы раз видели запросы GraphQL, написанные на его DSL, то Вы знаете, что запрос данных это запрос определённых полей объектов, которые мы хотим получить в результате. При этом структура результата такого запроса будет полностью соответствовать запросу и включать только те поля, которые были описаны в запросе. Но как именно понять, какие поля и объекты мы можем использовать в запросах? В этом нам поможет Схема.

Схема это основа GraphQL. Буквально:

Нет схемы - нет GraphQL.

Каждое GraphQL приложение определяет один объект типа Query (или Запрос), и, опционально, может определять объекты типа Mutation (или Мутации). Эти типы в GraphQL особенные - они определяют точки входа для каждого запроса в GraphQL. Буквально, если в REST API у нас есть URL схема доступа к данным, то в GraphQL запросы и мутации.

schema {
  query: Query
  mutation: Mutation
}

Query

Query определяет интерфейс доступа к Вашим данным. Запрос описывает какие типы объектов Вы можете прочитать из системы, какие параметры должны быть переданы в запросе и как именно должен выглядеть в результате ответ.

Примером Query может быть следующая схема:

query {
    documents(page: 0, size: 5) {
        id
        name
    }
}

В примере выше documents является названием метода во внутренней реализации приложения, который будет вызван. В терминологии GraphQL documents это поле.

page & size входные аргументы, которые будут использованы при вызове метода documents. Так же, Вы можете заметить какие именно значения аргументов будут переданы внутренней реализации.

Наконец, id & name названия полей объектов, которые мы хотим получить в ответ.

Запрос GraphQL может описывать несколько полей, каждое из которых будет иметь собственную сигнатуру. Например, давайте добавим дополнительное поле, которое представляет запрос документа по его идентификатору:

query {
    documents(page: 0, size: 5) {
        id
        name
    }

    document(id: ID) {
        id
        name
        createDate
    }
}

Обратите внимание, что входной аргумент поля document имеет тип ID. Это один из типов-скаляров, определённых в стандарте GraphQL. Мы поговорим об этом подробнее чуть позже.

Вывод поля document немного другой во втором случае. В списке присутствует ещё одно поле - createDate. Таким образом, объект, который будет возвращён в результате подобного запроса, будет описан этими 3 полями.

Ну и наконец, мы можем опустить ключевое слово query. Как было сказано выше, Query, это специальный тип, это своего рода точка входа в GraphQL. Если Вы не укажете ключеове слово query в определении своего запроса, GraphQL всё равно будет знать, что Вы имели ввиду query.

{
    documents(page: 0, size: 5) {
        id
        name
    }

    document(id: ID) {
        id
        name
        createDate
    }
}

В примере выше, мы определили наш GraphQL запрос. В нём есть одна странность, которую Вы могли заметить - если это запрос, то как же он будет работать? Мы определили 2 отдельных поля - в случае первого мы должны поискать первые 5 документов, и, в случае второго - мы должны найти один документ по идентификатору. И да, в GraphQL это абсолютно рабочий пример. Вы можете описать несколько методов доступа к данным в рамках одного запроса. И GraphQL обработает это. С точки зрения клиента это будет один http запрос на сервер, в результате которого, будет получен один ответ от сервера. Этот ответ будет содержать все данные, запрошенные в запросе.

Mutation

Mutation второй специальный тип в GraphQL. Мутации отвечают за описание интерфейса операций изменения данных. Например, продолжая пример с документами, чтобы добавить новый документ, можно описать следующую мутацию:

mutation addNewDocument($name: String, $docType: String) {
    addDocument(name: $name, docType: $docType) {
        id
        name
        createDate
        docType
    }
}

Subscription

Subscription (или подписка) это третий специальный тип в GraphQL. Query позволяют читать данные. Mutations позволяют писать или изменять данные. Но довольно часто, клиенты так же хотят получать обновления с сервера, тогда, когда данные в которых они заинтересованы изменяются. И как раз таки, за это, отвечает Subscription.

Типы

Как было отмечено ранее, Схема - главная концепция в GraphQL. Вы определяете собственную схему, описывая Ваши данные и операции чтения / изменения через поля и типы.

В GraphQL существует три типа данных, доступных для использования:

  • Специальные типы
  • Типы-скаляры
  • Объектные типы

Рисунок 1. Схема и типы

Любой тип, кроме специальных, имеет null в качестве значения по умолчанию. И, разумеется, есть возможность определить списочный тип, содержимым которого будут объекты типов-скаляров или объектных типов.

Query, Mutation и Subscription - это специальные типы. О них мы разговаривали выше.

Типы - скаляры

В GraphQL каждое поле должно иметь заранее определённый тип. Типы-скаляры это примитивные типы, определённые в спецификации GraphQL. Вот они:

  • String
  • Int
  • Float
  • Boolean
  • ID

Объектные типы

Объектные типы - это типы со своей сложной структурой и поведением. Они могут быть определены так же, как и Query - в схеме:

type RandomNum {
    random(max: Int): Int
}

Кастомные типы

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

К счастью, множество реализаций GraphQL для разных платформ (например, JVM) уже предлагают поддержку подобных типов.

Как добавить GraphQL?

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

Если кратко, то процесс интеграции GraphQL выглядит следующим образом: Рисунок 2. Интеграция GraphQL в проект

Совсем немного шагов требуется сделать перед тем, как Вы получите GraphQL в своё приложение. Кратко проговорим, что именно нужно сделать, простом примерем. Давайте притворимся, что мы создаём собственное GraphQL API для чтения книг в библиотеке.

  1. В первую очередь, мы должны определить схему. Схема моделирует наш бизнес домен через граф. Основной сущностью нашего домена является Book (книга). У каждой книги, есть Author (автор). Давайте смоделируем это в нашей GraphQL схеме:
type Book {
    year: Int!
    title: String!
    author: Author!
}

type Author {
    name: String!
    books: [Book]
}

Если помните, каждая GraphQL схема обязана включать объект типа Query. Давайте выразим два сценария чтения данных с помощью двух полей - когда мы выполняем поиск книги по названию и зачитываем все имеющиеся:

type Query {
    books:[Book]!
    books(title: Sring):[Book]!
}

Наша схема готова. Её нужно сохранить в файле с расширением graphqls.

  1. Далее, под капотом, в приложении, в которое мы интегрируем GraphQL, мы обязаны реализовать доступ к данным. Наше приложение должно содержать:
  • реализацию типов, определённых в нашей GraphQL схеме (Book & Author), чтобы GraphQL смог сопоставить типы даных из схемы, с типами данных самого приложения
  • реализацию так называемых resolvers. Resolvers это специальные сервисы, написанные на языке программирования приложения. Именно они отвечают за выполнение логики доступа к данным и их изменениям. Вы можете представить их, в качестве конкретной реализации интерфейсов, описанных в GraphQL схеме.
  1. Наконец, после того, как схема и resolver’ы будут готовы, можно начинать работать с GraphQL на стороне клиента приложения.

По умолчанию, GraphQL использует http. Единственный URL, /graphql, будет доступен в приложении и будет слушать входящие запросы. Так что, любой клиент, который хочет воспользоваться GraphQL, будет отправлять POST запросы на этот URL.

Чтобы запросить все доступные книги, без их автора, клиент может отправить следующий запрос:

{
    books {
        title
        year
    }
}

В результате, GraphQL API вернё список доступных книг, включая только их названия и дату публикации. Это может быть полезно в тех случаях, когда мы, например, должны показать список имеющихся книг пользователю, без каких-либо деталей. Однако, в случае, если книгу нужно показать со всеми, имеющимися деталями, можно использовать следующий запрос:

{
    books(title: "Java programming") {
        title
        year
        author {
            name
        }
    }
}

И этот запрос вернёт информацию о книге вместе с информаций о её авторе.

Список материалов

  • Основной веб-сайт GraphQL
  • Веб-сайт GraphQL Playground - Playground это отличный инструмент, которые может быть использован для тестирования интеграции приложения с GraphQL + выступать в качестве публичной документации для клиентов
  • Оригинал статьи - Данная статья, была написана мною для моего англоязычного блога. Если вдруг, интересно посмотреть оригинал, то его можно найти на моём личном сайте.