Что такое GraphQL: с основ до первых запросов

Это руководство по GraphQL. Из него вы узнаете базовую теорию, а также научитесь писать простые запросы с помощью GraphQL.
Что такое GraphQL: теоретический ликбез
Прежде чем рассматривать GraphQL, давайте уделим внимание исторической базе. Что такое SQL — structured query language или язык структурированных запросов?
SQL — декларативный язык программирования, который применяется для создания, изменения и управления данными в базах данных. Этот язык поддерживает четыре базовых оператора запросов: SELECT, INSERT, UPDATE и DELETE. С помощью SQL мы можем запросить из базы данных (БД) именно то, что нам необходимо.
Например, когда необходимо «достать» из БД всех пользователей с именем Maria, это можно сделать с помощью запроса:
Решить эту задачу с помощью REST можно несколькими способами:
- Определить endpoint на сервере, который будет отдавать из базы данных пользователей с
fname
Maria. - Определить общий endpoint для получения всех пользователей и фильтровать полученный список на стороне клиента.
При этом в каждом из вариантов есть свои минусы. Первый подход нельзя масштабировать, так как нет возможности создать endpoint для каждого пользователя. Если мы используем второй подход, повышается нагрузка на сеть, а также появляется необходимость в постобработке на стороне клиента.
А теперь представьте инструмент, который объединяет возможности SQL и REST на стороне клиента. Вы уже догадались, что он называется GraphQL. Этот инструмент берёт идеи, разработанные для манипуляции данными в БД, и использует их в вебе. Поэтому с помощью одного запроса GraphQL можно получить сразу все необходимые данные.
Ниже представлена информация, необходимая для начала работы с GraphQL.
Query: запросы в GraphQL
С помощью запросов GraphQL получает необходимые данные с сервера. Тип запроса Query в GraphQL — аналог GET в REST. Запросы — строки, которые отправляются в теле HTTP POST-запроса.
Примечание — Обратите внимание, все типы запросов в GraphQL отправляются через POST.
Query описывает данные, которые необходимо получить с сервера. Например, с помощью кода ниже можно получить fname
и age
всех пользователей в базе данных.
В ответ на этот запрос сервер присылает данные в формате JSON. Структура ответа соответствует структуре запроса.
Успешные операции возвращают JSON с ключом "data" и с ключом "error", а неуспешные возвращают JSON с ключом и сообщением об ошибке. Благодаря этому удобно обрабатывать ошибки на стороне клиента.
Mutation: мутации в GraphQL
Mutation — ещё один root types. С его помощью можно добавлять данные в БД. Mutation — аналог POST и PUT в REST. Ниже пример кода.
Здесь создаётся мутация createUser, которая добавляет в БД пользователя с fname
Richie и age
22. В ответ на этот запрос сервер присылает JSON с id записи. Ответ выглядит так:
Subscription: подписки в GraphQL
Subscription — третий тип операций в GraphQL. С его помощью клиент слушает изменения в БД в режиме реального времени. Под капотом подписки используют вебсокеты. Пример кода:
С помощью этого запроса можно получать список пользователей с именами и количеством лайков каждый раз, когда оно меняется.
Например, когда пользователь с fname
Richie получает лайк, ответ будет таким:
Подобный запрос можно использовать для обновления количества лайков в режиме реального времени в соответствующем интерфейсе, например, в форме с результатами голосования на сайте.
Выше представлены три основных типа запросов в GraphQL. Несмотря на поверхностное знакомство, вы получили достаточно знаний, чтобы начать работать со схемой GraphQL.
Как работать с сервером GraphQL
Рассмотрим ответ на этот вопрос на конкретном примере.
Цель: настроить сервер GraphQL, который отвечает на три типа запросов к БД NoSQL, в которой есть список пользователей с такой структурой:
Также в базе данных есть списки постов, которые опубликовали пользователи.
Для дальнейшей работы понадобится сервер Apollo. Это сервер GraphQL с открытым исходным кодом.
Настройте проект и установите зависимости:
Вы создали пустой проект. Теперь установите GraphQL, Apollo и nodemon для отслеживания изменений в файлах.
Чтобы nodemon работал, добавьте в package.json
запись:
"scripts": "start": "nodemon -e js, json, graphql"
База данных
Данные в этом примере поместим в переменную JSON. В реальных проектах данные обычно хранятся в БД. В тестовом проекте данные хранятся в index.js. Вот они:
Теперь можно перейти к работе с сервером GraphQL.
Схема
Работа с сервером GraphQL всегда начинается с разработки схемы (Schema). Она состоит из двух взаимосвязанных объектов: TypeDefs и Resolvers.
Выше были описаны основные типы GraphQL. Чтобы сервер мог с ними работать, эти типы необходимо определить. Объект typeDef определяет список типов, которые доступны в проекте. Код выглядит так:
В примере выше определяется тип User, в котором указываются fname
, age
, likes
и другие данные. Для каждого поля определяется тип данных: String
или Int
. GraphQL поддерживает четыре типа данных: String
, Int
, Float
, Boolean
. Если в поле указан восклицательный знак, оно становится обязательным.
Также в примере выше определяются типы Query
,Mutation
и Subscription
.
Первый тип, который содержит внутри себя тип Query
, называется users
. Он принимает id и возвращает объект с данными соответствующего пользователя. Это обязательное поле. Ещё один тип Query
называется posts
. Он устроен так же, как users
.
Тип Mutation
называется incrementLike
. Он принимает параметр fname
и возвращает список пользователей.
Тип Subscription
называется listenLikes
. Он возвращает список пользователей.
После определения типов необходимо добавить их логику. Это нужно, чтобы сервер знал, как отвечать на запросы клиента. Эта задача решается с помощью Resolvers.
Также полезно
Когда Gatsby заменит WordPress: интервью с GraphQL-гуру Михаилом Новиковым.
Resolver
Resolver или распознаватель — функция, которая возвращает данные для определённого поля. Resolver’ы возвращают данные того типа, который определён в схеме. Распознаватели могут быть асинхронными. С их помощью можно получать данные из REST API, базы данных или другого источника.
Определим Resolver’ы:
В примере выше есть шесть функций:
- запрос
users
возвращает объект пользователя, соответствующий переданному id; - запрос
posts
возвращает объект поста, соответствующий переданному id; - в поле
posts
User
распознаватель принимает данные пользователя и возвращает список его постов; - в поле
user
Posts
функция принимает данные поста и возвращает пользователя, который опубликовал пост; - мутация
incrementLike
изменяет объектusers
: увеличивает количествоlikes
для пользователя с соответствующимfname
. После этогоusers
публикуются вpubsub
с названиемLIKES
; - подписка
listenLikes
слушаетLIKES
и отвечает при обновленииpubsub
.
Два слова о pubsub
. Этот инструмент представляет собой систему передачи информации в режиме реального времени с использованием вебсокетов. pubsub
удобно использовать, так как всё, что касается вебсокетов, вынесено в отдельные абстракции.
Итак, работа с типами и распознавателями завершена. Теперь можно запустить сервер.
Запускаем сервер
Откройте http://localhost
/ в браузере. Благодаря Apollo вы можете полноценно протестировать сервер GraphQL.Потратили 15 минут на настройку сервера и, вуаля, написали первый запрос. Полная версия кода опубликована здесь.
REST vs. GraphQL
Что делать, если нам нужно найти пользователя, который опубликовал пост с id x? Рассмотрим, как эта задача решается с помощью REST. Вот данные:
Users:
{
"id": 1,
"fname": "Richie",
"age": 27,
"likes": 8
},
{
"id": 2,
"fname": "Betty",
"age": 20,
"likes": 205
},
{
"id" : 3,
"fname": "Joe",
"age": 28,
"likes": 10
}
Posts:
{
id: 1,
userId: 2,
body: "Hello how are you?"
},
{
id: 1,
userId: 3,
body: "What's up?"
},
{
id: 1,
userId: 1,
body: "Let's learn GraphQL"
}
Сначала необходимо определить endpoint’ы GET:
> https://localhost:4000/users/:id
> https://localhost:4000/posts/:id
Когда необходимо получить данные о пользователе, который опубликовал пост с id 1, запрос выглядит так:
GET https://localhost:4000/posts/1
Response:
{
id: 1,
userId: 2,
body: "Hello how are you?"
}
GET https://localhost:4000/users/2
Response:
{
"id": 2,
"fname": "Betty",
"age": 20,
"likes": 205
}
Чтобы получить нужные данные, приходится дважды обращаться к серверу.
Что делать, если нужно получить только имя пользователя? Конечно, можно использовать данные поля fname
. Но мы уже получили дополнительные данные: id
, likes
, age
. Операция становится слишком дорогой.
Можно попробовать использовать endpoint, который получает только данные из поля fname
:
https://localhost:4000/userByfname/:id
Это решает проблему. Но сложность в том, что вам придётся создавать endpoint для каждого типа информации. Это неудобно.
Давайте посмотрим, как можно решить задачу с помощью GraphQL. Всё, что вам требуется — простой запрос:
Если нужно получить age
пользователя, который опубликовал пост с id 1, запрос будет таким:
Поле user
в запросе определено в схеме. Поэтому вы можете получать нужную информацию без использования endpoint’ов и повторных обращений к серверу.
Изучайте Node.js на Хекслете
Первые курсы в профессии «Node.js-программист» доступны бесплатно. Регистрируйтесь и начинайте учиться.
Заключение
В руководстве рассмотрена базовая информация о GraphQL: краткая теория, типы, отличия от REST. В комментариях можно поделиться впечатлениями о GraphQL и опытом применения этого инструмента.
Адаптированный перевод статьи So, What the Heck is GraphQL by Karthik Kalyanaraman. Мнение автора оригинальной публикации может не совпадать с мнением администрации «Хекслета».