Сегодня в среде разработчиков часто продвигают GraphQL в качестве замены REST, хотя обе технологии можно использовать одновременно. В этой статье Анастасия Иванова, технический писатель платформы МТС Exolve (входит в экосистему МТС), рассмотрит интерфейсы подробнее, чтобы понять, как выбрать подходящее решение под каждый конкретный проект. Подробности — под катом.
REST API: стандарт и его особенности
Веб-службы применяют интерфейс программирования REST (Representational State Transfer — «передача состояния представления») API для предоставления клиенту запрошенных им данных.
REST API — архитектурный стиль. Он использует HTTP-запросы для доступа к различной информации. Интерфейс работает с пятью основными запросами:
GET
PUT
POST
PATCH
DELETE
Ответы возвращает в форматах XML, YAML и JSON. С помощью REST API клиент может вносить изменения на сервер.
Запросы REST API состоят из нескольких блоков кода:
Endpoint. Элемент содержит URL — способность идентифицировать ресурс в онлайн-режиме
HTTP-method. Метод описывает тип запроса, который будет отправлен на сервер
Header. Нужен для серверов, чтобы они могли тестировать, проводить аутентификацию и кэшировать данные
Body. Тело кода содержит полезные данные для передачи на сервер
Недостатки
Основным минусом REST API считают создание множества эндпоинтов. Работать с ним — это как уточнять что-либо в разных компаниях. Нужно звонить всем по очереди.
Например, вы хотите посмотреть варианты пиццы и указываете точку /pizza. API принесёт информацию о цене, акциях и составе продукта. Но вы просили информацию о видах пиццы. Придётся постоянно прописывать конкретные адреса, а это энергозатратно для разработчиков и проблематично для пользователей.
То есть возникают проблемы с выборкой: она либо избыточна, либо недостаточна. REST не может предоставлять точные данные по запросу клиентов, потому что использует один endpoint для одного адреса.
Архитектурный стиль GraphQL позволяет сделать запросы за один раз. Клиент передаёт названия трёх мест, запрашивает нужные данные и ждёт. GraphQL выполнит все запросы и принесёт данные из разных адресов.
GraphQL: стандарт и его особенности
GraphQL — язык запросов для управления данными графов. Он использует разные виды протоколов для передачи информации. GraphQL в основном возвращает ответы в JSON.
Главное отличие GraphQL от REST API в том, что все данные клиент может выбрать всего одним запросом, даже если они будут располагаться в разных источниках. А в REST API придётся сделать выборку и извлекать их уже оттуда.
Разработчики считают GraphQL умным агрегатором, который играет роль оркестратора. Он переадресовывает фрагменты запроса на разные эндпоинты. GraphQL умеет отправлять данные поверх HTTP-протокола, Websocket и SSH. Это удобно, когда вы разрабатываете многофункциональное приложение.
GraphQL имеет три главных блока:
Queries — запросы
Schema — схема. Описание типов объектов и полей, принадлежащих каждому объекту
Resolvers — функция, отвечающая за заполнение информацией полей в схеме. Способ выполнения процедуры определяет разработчик. Например, резолвер может извлекать данные из сервера или стороннего API
Для работы с графовым языком запросов есть утилита GraphiQL. Разработчик интегрирует её с Endpoint GraphQL. Он отправляет запрос серверу на предоставление своей схемы — вы получаете интерфейс для проведения тестов и изучения запросов.
Если с формированием запросов всё понятно, давайте посмотрим основные блоки кода для схем:
Character — вид среды GraphQL
Fields (например, name и address) — поля, заполняемые в виде объекта Character. Их находят в ответе на запрос. Каждое поле возвращает то значение, которое обрабатывает
Разберём поля подробно, потому что они составляют значительную часть возвращаемой информации. Итак, они могут быть скалярного вида, объектом, типом ввода, перечисления, объединения и интерфейсом.
Например, скалярные виды — это:
String — определённые символы в коде UTF-8
Int — 32-битное целое число
Float — цифра с плавающим разделительным знаком
Boolean — логическая информация: true или false
ID — уникальный идентификатор. Нужен для повторной выборки объекта
GraphQL умеет анализировать синтаксис и проверять формы собственной схемой. Этот API может показаться сложным на стадии создания приложения, но подобное разделение на отдельные элементы — интересная возможность для анализа синтаксиса — позволяет получать чёткие ответы на запросы без дополнительных выборок, в отличие от REST API.
На что обратить внимание при выборе REST API или GraphQL
В доступных сегодня REST API вы чаще можете встретить API как список endpoints:
GET /animals/:id
GET /humans/:id
GET /animals/:id/comments
POST /animals/:id/comments
В GraphQL же не нужны URL-адреса для идентификации доступных данных в API. Вы используете GraphQL-схему:
type Query {
animal(id: ID!): Animal
human(id: ID!): Human
}
type Mutation {
addComment(input: AddCommentInput): Comment
}
type Animal { ... }
type Human { ... }
type Comment { ... }
input AddCommentInput { ... }
Если чуть вникнуть, то различия такие:
GraphQL позволяет переходить внутри одиночного запроса от точки входа к данным, следуя связям из схемы. В REST получить связанные ресурсы возможно через запросы к нескольким endpoints
В GraphQL нет отличий между полями типа Query и иными полями. То есть в запросе любое поле может работать с аргументами. В REST нет какого-то первого класса при вложенном URL
В REST запись данных вертится вокруг HTTP-методов, достаточно менять GET на POST. В GraphQL нужно менять ключевое слово в запросе
REST API зашифровывает данные и даёт множество вариантов самостоятельной аутентификации API без помощи разработчиков. GraphQL позволяет контролировать доступ к полям и операциям в запросах. Но для защиты информации придётся создавать дополнительные методы.
Из-за жёсткой структуры REST API нужно постоянно прописывать путь к данным, которые хочет получить клиент, делая выборку. При работе с GraphQL достаточно прописать запрос один раз, чтобы получить точный ответ на него. Это более гибкий инструмент для выборки.
Эндпоинты запроса GET REST API могут попасть в кэш браузера на стороне клиента, на сервере — через CDN. GraphQL не хранит кэш. Однако, если использовать инструменты Apollo или URQL, можно хранить кэш на стороне клиента.
Спецификация GraphQL не подразумевает загрузку файлов, поэтому реализация остаётся на ваше усмотрение. Есть такие варианты:
Base64: делает запрос больше и дороже для кодирования и декодирования
отдельный сервер или API
библиотека типа graphql-upload, которая реализует спецификацию многочастного запроса GraphQL
В REST API каждый код состояния — это определённый вид ответа. Клиент, обрабатывающий ответы, должен знать названия кода и значение.
У GraphQL всё проще. Неважно, что будет в ответе, «успех» или «неудача», система пропишет один код состояния — 200 ОК. Клиент использует специальные инструменты для более точной расшифровки. Все ошибки будут обработаны как часть тела в рамках объекта Errors.
Status Code |
REST |
GraphQL |
200 |
Ok |
Ok |
400 |
Bad Request |
- |
401 |
Unauthorized |
- |
Пример заказа бургера по SMS
Представьте себе процесс заказа бургеров. Кто-то уже видел этот мем про GraphQL.
Вы заходите в ресторан и заказываете чизбургер. Независимо от количества заказов (вызовов RESTful API), вы каждый раз получаете ингредиент чизбургера. Он всегда будет одинаковой формы и размера (это ответ REST).
https://api.com/cheeseburger/
С GraphQL вы можете построить его по-своему: указать, каким именно чизбургер вы хотите видеть. Сначала булочка сверху, затем котлета, солёные огурцы, лук и сыр (если вы не веган).
query getCheeseburger ($vegan: Boolean) {
cheeseburger {
bun
patty
pickle
onion
cheese @skip(if: $vegan)
}
}
Ваш ответ — это именно то, что вы хотели — ни больше, ни меньше. Затем взять и заказать бургер, отправив SMS в ресторан.
Ресторан получает SMS через омниканальную платформу МТС Exolve, заранее настроив приём подобных заказов по разным каналам.
При использовании REST вы, скорее всего, получите в ответ полные «наборы данных». Если запросите информацию для меню, ваши запросы будут такими:
запрос menu бургеров по названиям, описаниям, ингредиентам в одном запросе
запрос prices к этому меню, в другом запросе
запрос images для меню
и так далее
REST даёт вам чизбургер, который есть в меню ресторана, но GraphQL позволяет вам модифицировать его и получить именно то, что вы хотите. Для вас важно выбрать оптимальный путь и заранее спланировать возможные связки с коммуникациями, тем же SMS API.
Что использовать в работе
Итак, REST API применяют, если:
работают с небольшими программами, где нужно обрабатывать простые данные
пользователи работают с ними одинаково
нет требований к сложным запросам
GraphQL выбирают, когда:
у серверного оборудования маленькая пропускная способность. Необходимо снизить количество ввода-вывода данных
в одном адресе нужно объединить множество источников
запросы пользователей различны, необходимо прорабатывать разные ответы
REST API |
GraphQL |
Поддержка версий API |
Нет поддержки версий API |
Есть кэширование |
Нет кэширования |
Для развёртывания использует URL-адреса |
Развёртывание по HTTP с одним эндпоинтом |
Вывод ответа обычно в XML, JSON и YAML |
Вывод ответа в формате JSON |
Архитектуру предоставляет сервер |
Архитектуру предоставляет клиент |
Быстрая обработка ошибок |
Сложная обработка ошибок |
Варианты для документации |
Один документ |
Для упрощения работы требуется дорогостоящее оборудование |
Возможность сшивать схемы и получать удалённые данные |
Заключение
В этой статье мы подробно указали на различия двух интерфейсов и описали варианты их использования. Теперь вы можете применить тот стиль архитектуры, который подходит для вашего приложения лучше всего. Или задействовать комбинацию, особенно когда у вас в руках очень разные по строению системы и нужна интеграция с SMS API и подобными дополнениями.
Помните:
при написании публичного API, в рамках которого вы планируете обеспечить пользователю широту действий, используйте GraphQL
при разработке frontend-приложения применяйте REST API. Если хотите избавить пользователя в публичном API от N+1 проблем, используйте REST
Комментарии (12)
catBasilio
10.10.2023 14:26Пользуясь случаем, задам вопрос.
Как в чарлзе мокать GraphQL запросы? с REST там по URL оно прекрасно матчится, а что с GraphQL делать? там эндпоинт-то всегда один.
Batalmv
10.10.2023 14:26Когда-то разбирался, клиент хотел на GraphQL. Как по мне, главные:
Замена одного запроса один, что дает выигрыш в "скорости", и возможность гибко задавать что именно надо сейчас. REST API структурирован. Да, он может быть использован как транспорт для передачи "объектов" запрос, но это уже выглядит сильно натянуто, плюс бэкенд надо ж как-то написать
Наличие "акселераторов", которые ускоряют разработку решения в целом
Возможно статья не смогла раскрыть это акцентировано, но ... лично мне зашло, жалко проект не взлетел
nergal-perm
10.10.2023 14:26+1Расскажите, пожалуйста, как происходит процесс изменения схемы GraphQL. Вот, к примеру, есть система "на бою", у нее сотни клиентских приложений, у каждого клиентского приложения тысячи юзеров. И в какой-то момент какое-то поле в схеме GraphQL (скажем, идентификатор клиентского приложения, оно во всех запросах присутствует) вдруг должно быть изменено. Скажем, раньше это был простой строковый идентификатор, а теперь нужно передавать объект из двух полей: GUID и строковый идентификатор. Как это изменение должно выкатиться на прод, не задев все клиентские приложения?
Как Вы в своих реальных рабочих проектах с этим справляетесь?
Acionyx
10.10.2023 14:26+2Обычно это организационный процесс в первую очередь. Можно добавить новое поле, старое пометить deprecated и запланировать удаление через 1-3 года. Из минусов - бекенд одновременно поддерживает два поля. Из плюсов - обратная совместимость и контролируемый процесс перехода.
Разницы между GraphQL и REST в данном случае нету, подход и там и там применим.
Сами разработчики GraphQL как раз не поддерживают идею api versioning и смотрят в сторону mutable схемы в общем случае.
dph
10.10.2023 14:26+3После прочтения статьи сложилось устойчивое ощущение, что автор не разбирается ни в REST API, ни в GraphQL. Плюсы и минусы к реальности не имеют никакого отношения, про json-rpc автор, похоже, вообще не слышал, про проблемы мутаций составных объектов даже не догадывается, в каждом абзаце - фактическая ошибка.
Для технического писателя это нормально, но зачем технический писатель вообще пишет подобные статьи самостоятельно?
Интересно, а в МТС догадываются, как именно подобные статьи влияют на бренд компании? Я бы побоялся даже как пользователь связываться с компанией с таким уровнем публичных статей, про работать я даже не говорю....
savostin
Как по мне, не увидел никаких преимуществ GraphQL, кроме возможности менять запросы на стороне клиента, не переписывая сервер. Впрочем, не такой это и плюс.
Сдается мне, это придумали фронтедщики, чтоб не пинать бэкендщиков по каждому чиху.
Вот только мне кажется при такой свободе и отсутствии кеширования такие запросы, особенно сложные, сильно просаживают быстродействие.
PrinceKorwin
Плюсы GraphQL:
Единая нотация
Возможность указания только тех полей, что нужно
Можно за один запрос получить данные с разных backend-ов
Можно получать связанные данные (пользователь + его заказы, например)
Запросы к бекендам могут параллелиться без изменения логики в клиенте
Но есть и минусы:
Секурити, точнее отсутствие
Своя нотация запросов
Нельзя указать * для полей - обязательно перечислять
Скорость ниже, чем прямой вызов (но может быть быстрее за счёт параллелизма)
Вообще для UI подход GraphQL оказался очень удобным даже не смотря на минусы.
action52champion
А в чем плюс? Только возня с ее описанием. В случае простых рест запросов можно вообще забить на нотации и трафик и слать объекты целиком, с посылом - все что есть в ответе есть и в объекте. В случае сложных запросов десять раз подумаешь, а не проще ли было сделать конкретный ендпоинт для этого запроса, который бы слал конкретные объекты предстваления, не завязанные на схему, а нужные только для представления.
В REST это тоже делалось задолго до создания GraphQL. Параметр fields в конце URL.
Обожаю этот аргумент! Вы когда-нибудь писали кастомный резолвер, который вам соберет сложный объект по нескольким микросервисам? Это одинаковая головная боль, что для REST, что для GraphQL. Только в случае последнего вам придется писать резольвер по-настоящему общего назначения, который будет работать для всех полей и связанных объектов, и потом решать проблемы производительности с этим чудом. В случае REST такие вещи хотя бы ограничивают универсальность применения одним ендпоинтом и доступными для него спидхаками и упрощениями выборки.
Да можно, вопрос какой ценой нагрузки на сервер. Вероятно, можно написать хаки для ресольвера, чтобы он какие-то случаи запросов покрывал джойнами, а не выборкой связанных данных отдельными подзапросами. Но чем такие оптимизации принципиально отличаются от создания специализированного рестового ендпоинта?
Чтобы что?
savostin
Просто в REST со стандартом проблема, его по сути нет. Есть рекомендации и устоявшаяся практика. Чем Вам Method+URL+параметр не нотация?
В REST вы тоже можете собирать с многих сервисов. Некоторые даже делают это в API gateway.
А вот возможность получить на сервер запрос всех полей и связанных с ними записей думаю сильно плохо скажется на его здоровье. Интересно посмотреть статистику отказа в обслуживании таких endpoint’ов.
action52champion
Потому что "нотация" GraphQL создана из рассчета, что у нас волшебный сервер, который одинаково эффективно возвращает запрошенные данные. И пофиг как там GraphQL ложится на внутреннюю предметную область, аппаратные ограничения, уже реализованные способы доступа к объектам. За исключением простых случаев, ситуация когда фронтендщики диктуют схему доступа к данным, пусть и прекрываясь технологией, черевата проблемами с производительностью и неоптимальной организацией хранения данных. Потому что для фронтендщика не будут видны под капотом ключи MySQL, лимиты на внешние апи, внутренная организация кэширования. И REST, на мой взгляд, куда лучше справляется как с учетом этих особенностей, так и защитой фронтеднщиков от этих ненужных деталей.
К тому же олновесная реализация нотации GraphQL для всех сущностей системы может стоить дорого. А реализация ограниченного подмножества будет по сути не особо отличаться от REST. Только вместо ендпоинтов для сущностей нужно будет создавать ресолверы для новых сущностей.
Если пытаться на ендпоинте сделать логику выборки из базы как в авторезолверах, строящих запросы по аннотациям в ORM, то все может быть очень печально. А если сделать выборку вручную написанными запросами, созданными специально под конкретный флоу, то все может быть в разы лучше чем у GraphQL.
space2pacman
Если говорить про сторонний API на graphQL то удобство в том, что я могу
1) Получать нужные мне данные и их количество из запроса.
2) Комбинировать данные в одном запросе.
Если про свои проекты то там обычный REST API
TyVik
Собственно, вы правы. Изначально GraphQL был придуман в Facebook, чтобы меньше зависеть от бэкенда. Там же куча сервсов, а к каждому и не один запрос - вот и придумали гейтвей, на который можно было бы переложить эту сложность.