Привет! Сегодня у нашей статьи два автора — бэкенд-разработчик Артём и фронтенд-разработчик Илья.  Примерно год назад мы решили попробовать ввести graphQL в свой проект и сейчас хотим поделиться, как это происходило. Расскажем, что такое GraphQL, как его внедряли, почему мы вообще решили с ним подружиться и как начать взаимодействовать с API бэкенда словно вы граф, а не холоп.  Если лень читать или больше нравится видеоформат — вам сюда.  Для начала давайте рассмотрим предметную область нашего приложения – это поможет понять, с какими проблемами мы столкнулись.  Мы разрабатываем отдельный от основного hh.ru продукт — внешнюю CRM-систему по подбору персонала Talantix. Если hh.ru, как и ряд схожих продуктов, это скорее job-борда, где кандидат создает резюме, а работодатель создает вакансию, на которую можно откликнуться, то после этого обычно в бой вступает CRM-система, где создаются интервью-встречи, кандидаты переводятся по кастомным этапам, строится аналитика работы рекрутеров и происходит прочий очень занимательный для hr флоу работы. И чтобы подобраться к задачам, которые решает GraphQL, попробуем спроектировать API для нашей системы и заодно посмотрим, какие могут быть проблемы взаимодействия этого API с бэкендом.  Итак, мы зашли на сайт и собираемся создать встречу на интервью.  Помимо всего прочего нам понадобятся ФИО и контакты кандидатов. Сделаем стандартный по REST GET-метод /candidates , который будет возвращать необходимые данные.  Однако в нашей системе данные о кандидатах нужны не только на странице создания встречи. Ещё есть отдельная страница кандидатов, например, где помимо ФИО нам также понадобятся последний опыт работы и зарплата из резюме.   Окей, можем сделать ещё один GET-метод по REST, назовём его /resumes и по id резюме получим необходимые данные.   Круто, но тогда появляется особенность, которая называется underfetching — мы не можем получить все данные за один запрос и ходим на бэкенд ещё раз. И это действительно иногда может стать проблемой — мы делаем несколько сетевых походов для получения данных на одной странице, не у всех пользователей может быть стабильный интернет, мы теряем в производительности. Ну что же, мы можем забить на REST, создать единый метод /candidates, который будет возвращать все данные из сущностей кандидатов и резюме. Мы же знаем как и где они будут использоваться, так чего бы в один метод не напихать туда всё, что нам нужно?  Не всё так просто. При создании встречи нам не нужны данные о резюме. Эта проблема тоже имеет значение, и она называется overfetching. Мы отдаём избыточные данные, которые никак не будут использоваться – снова теряем в производительности. Можно, например, создать методы /candidates_min и /candidates_max. С точки зрения производительности всё ок, но не очень масштабируемо - а если добавится третий метод, как назовём? Но у нас есть знание какие данные нужны конкретным страницам - можно им и воспользоваться. Можем сделать отдельный слой на бэкенде, который будет ходить в разные микросервисы, аккумулировать данные и возвращать в соответствии с определенной страницей. Вариант хороший, но есть свои минусы. Например, на страницах с почти идентичными данными придётся дублировать логику с походами в другие сервисы и сбора информации. Да и если появится ещё одна страница со схожими данными придётся всё равно создавать новую страницу на бэкенде - занимает время разработки. Есть ещё один вариант — оставить метод /candidates и в query-параметрах передавать только те поля, которые нам нужны.  /candidates?fetchEmail=true&fetchPhone=true&fetchResumeSalary=true… Однако и это решение нам не полностью подходит — таких полей могут быть сотни и тысячи - сложно разобраться, сложно контролировать, сложно поддерживать. И вот тут мы плавно подходим к самой сути GraphQL.  GraphQL позволяет декларативно на клиенте описать, какие данные ему нужны, и бэкенд вернет только их. Это значит, что клиент сам говорит, какие данные он хочет получить. Например, как в sql-запросах, когда работаем с таблицами: перечисляем нужные поля и откуда их взять.  А со стороны бэкенда — это составление некой схемы, которой будут следовать запросы с клиента (опять же, аналог составления таблицы в sql), и разработка неких распознавателей, которые будут понимать, куда ходить за данными и что с ними делать. То есть на каждое поле объекта можно написать свой метод распознавателя, в котором будет содержаться его бизнес-логика. GraphQL часто путают с базой данных или SQL, хотя мы только что убедились, что это совсем другой уровень абстракции. Более того, у нас теперь будет только один endpoint — POST-метод, который займется обслуживанием вообще всех запросов GraphQL, в отличие от того же REST. А значит, GraphQL — это стандарт языка описания запросов от клиента к бэкенду. Опишем краткую схему для нашего случая и заодно поймем, где граф в GraphQL. Схема описывается на специальном языке GraphQL-схемы, и он чем-то похож на JSON. Мы описали кандидата, резюме и соединили их вместе. А вот и Graph в GraphQL — мы объявили ноды графа и соединили их.  Однако войти в этот граф пока нельзя. Поэтому опишем входные точки. В данном случае будет одна точка — candidates, например, с фильтром по id.  И вот почему GraphQL — это QL (query language): запрос очень похож на JSON, но на самом деле это не он. И после этого сервер возвращает нам нужные данные. В REST: N ресурсов; Бэкенд решает, какие данные выдать; Нет строго контракта, вернее, контракт на уровне согласования; Обработка ошибок через коды ответа 4**-5**. В GraphQL: 1 POST-ресурс; Клиент решает, какие данные запросить; Контракт диктуется схемой и имеет строгую типизацию; Обработка ошибок через массив errors в теле ответа — это обусловлено тем, что graphQL не завязан на определённый протокол. REST GraphQL N ресурсов 1 POST-ресурс Бэкенд решает, какие данные выдать Клиент решает, какие данные запросить Нет строгого контракта Контракт диктуется схемой Обработка ошибок через коды ответа Обработка ошибок через массив errors в теле ответа  Итак, мы начали переходить на GraphQL. На самом деле стратегия была довольно простой — мы брали определённые страницы и переводили на новый формат. Поскольку мы сомневались, зайдёт ли нам GraphQL, политика была следующая: мы переводили высоконагруженные страницы со сложной логикой, чтобы сразу окунуться на всю катушку и понять, какие у нас могут быть проблемы и действительно ли помогает GraphQL.  В промежуточных стадиях у нас могут быть 2 версии однотипного кода (legacy и graphQL), пока полностью не переведём целую сущность — это нормально, если в коде сразу объявить deprecated legacy-методы и не забивать с переводом остальной функциональности. В hh.ru рекомендуется делать 30% техналога на команду и мы воспользовались этой возможностью. Также мы ставили себе технические цели в OKR, чтобы лучше мониторить прогресс. Важное допущение — мы переводим пока что только GET-методы, хоть и graphQL позволяет изменять сущности. Это сделано только потому, что пока что перевод GET-методов для нас более актуален.  Мы сидим на GraphQL с осени прошлого года и продолжаем развивать этот протокол. Вот какие преимущества нам удалось обнаружить за это время: Декларативное общение клиент-сервера и решение underfetching/overfetching проблем. Единый API, который планируем расшарить не только для web-клиента, но и на пользователей. Есть вероятность, что это ускорит разработку: при редизайне страницы в идеальном мире со стороны бэкенда ничего не нужно делать. Бэкенд работает по строго типизированной схеме — и, как следствие, получаем документированное API из коробки. Поменяли подход к проектировании задачи — теперь мы проектируем API, а не страницы.  Также мы встретились и с некоторыми трудностями. Клиент теперь сам готовит данные. Мы говорили, что это хорошо, но есть нюанс. Клиент может запросить вообще все данные в любом количестве и бэкенд тогда слегка приляжет. Поэтому нужно уметь контролировать внешние запросы. И у GraphQL есть решение — мы можем навешивать веса на определенные поля, и если сумма весов превысила заданную константу, GraphQL зареджектит этот запрос. Этот подход нормально работает, но не всегда получается сделать гибко и удобно. Экосистема Java. Всё не так плохо, но java-мир скорее на догоняющей позиции. Обсуждение java-фреймворков мы решили вынести в отдельную статью, а видео уже можно посмотреть тут. Построение инфраструктуры. Мониторинг, обработка ошибок, встраивание в текущую инфру — всё это реально, но во всё это нужно вкладываться. По внедрению инфраструктуры graphQL на фронтенде можно посмотреть тут. Редиректы теперь намного сложнее Кеширование запросов непонятно как реализовать Ретраи — не очень понятно, что теперь идемпотентный запрос, а что нет, и какие запросы можно ретраить Работа с неструктурированными данными — graphQL не совсем предназначен для заливки и выдачи файлов, например  В общем, минусов тоже хватает, поэтому мы бы не советовали рассматривать GraphQL как следующую ступень эволюции работы с API после REST. Хотя, если вы начнёте гуглить про graphQL, то увидите пёстрые статьи о том, как он "уничтожает REST целиком и полностью". Этому не стоит верить. GraphQL  — “всего лишь” альтернатива REST со своими болячками. Просто посчитайте, даёт ли он вам больше плюсов, чем минусов, оцените риски и только после этого принимайте решение — внедрять его или нет. Мы внедрили и нам понравилось. Вот мы и поговорили в общих чертах про GraphQL, как мы его внедряли и на какие проблемы напоролись. Эта статья — только начало весёлого цикла про GraphQL. Не переключайтесь! Ну и пишите в комментах, пробовали ли вы подружиться с GraphQL и чем это кончилось, нам интересно.