Друзья, всем привет!

Эта статья в первую очередь будет интересна фронтенд-разработчикам, особенно тем кому актуальна тема GraphQL, но в то же время я надеюсь, что она будет полезна и бэкенд-разработчикам и поможет им понять преимущества GraphQL глазами фронтендера.

Несколько слов о себе: Я являюсь VP of Technology компании 8base. Мы разрабатываем инструменты, которые позволяют фронтенд и мобильным разработчикам быстрее создавать качественные бизнес-приложения.

Мы предоставляем платформу, в которой достаточно определить модель данных через наш UI и получить готовый GraphQL API для взаимодействия с данными. Пользователь получает настроенный сервер с ролями, правами доступа, возможностью работать с файлами, деплоить serveless-функции, CI и многое другое. Также для ускорения разработки у нас есть SDK и готовые библиотеки компонентов для React. Бэкенд пишем на Node.js


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

Почему же всё-таки GraphQL?


В отличие от REST, каждый API на GraphQL предоставляет схему (schema), которая содержит информацию о входных и выходных типах данных, их полях и взаимосвязях между типами. Это позволяет использовать в API модели данных со связями, а также совершать гибкие запросы (queries) и изменения в данных (mutations), которые не зависят от представления в конкретном клиентском приложении.

Кроме того, GraphQL API позволяет клиентским разработчикам явно определять возвращаемые данные, в том числе ссылаться на связанные объекты в каждом запросе, не допускать избытка и недостатка данных (over- and under-fetching) и сокращать количество запросов к API.

Сравним GraphQL и REST


Давайте сравним GraphQL и REST на простом примере — создании интернет-блога. При выводе на экран отдельной статьи в блоге нам надо показать содержимое статьи, аватар и имя автора, список комментариев, а также аватар и имя каждого комментатора.

Если использовать REST, нам понадобятся следующие запросы к API:

GET /articles/:id — получить содержание статьи;
GET /users/:id — получить URL аватара и имя автора;
GET /articles/:id/comments — получить список комментариев (а если бэкенд-разработчики не встроили в него имена и аватары комментаторов, придется выполнить еще и шаг 4);
GET /users/:id для каждого комментария — получить URL аватара и имя комментатора.

Обратите внимание на проблему under-fetching (когда один запрос не возвращает все что нужно) и over-fetching (когда, к примеру, запрос /users/:id возвращает больше данных, чем будет отображаться), а также на более сложный код во фронтэнде, необходимый для организации серии запросов к API.

В GraphQL вся информация может быть получена одним запросом, при условии что разработчики API указали взаимосвязи между статьями, пользователями и комментариями в схеме GraphQL API. Кроме того, ответ от API будет содержать только запрошенные поля, что уменьшит его объем.

Вот пример GraphQL-запроса, который возвратит нам сразу всю информацию в одном запросе:

GraphQL API позволяет получать все данные в одном запросе

Чтобы реализовать такую возможность в REST, бэкенд-разработчикам потребовалось бы создать особый API endpoint, который возвращал бы эту информацию. Однако то же самое пришлось бы делать для каждого подобного случая с отображением данных из связанных таблиц, а заодно и обновлять API, как только фронтенду понадобятся дополнительные поля.

Этот пример показывает, как GraphQL выравнивает «баланс сил» в пользу фронтенд-разработчиков, позволяя им запрашивать любые необходимые данные, и дает бэкенд-разработчикам возможность реализовывать API так, чтобы охватить в нем всю предметную область, а не конкретное представление на клиенте.

Все это в сочетании с набором библиотек и инструментов выводит качество и удобство работы фронтенд-разработчика на новый уровень — именно поэтому популярность GraphQL среди команд разработки растет по всему миру.

Как это выглядит на практике


Один из недостатков GraphQL — более сложная реализация, чем у REST. Чтобы представить данные в удобном для самых разных клиентских приложений виде, необходим большой объем работы на бэкенде. Реализовывать CRUD-операции, связи, фильтры, сортировку и разбивку на страницы для каждого типа данных довольно утомительно. Организовать такие важные возможности, как права доступа к данным и прикрепление файлов к объектам данных, тоже не так-то просто.

Сейчас я покажу какие возможности дает использование GraphQL на примере нашей реализации:

  • получить один объект по идентификатору или любому уникальному полю

  • получить список объектов на основе фильтров, сортировки, разбивки на страницы и критериев полнотекстового поиска

  • создать, обновить или удалить объект

  • подписаться на события данных в реальном времени через веб сокет

  • все эти операции отлично работают со взаимосвязями между объектами, поэтому дочерние объекты можно запрашивать, создавать и обновлять вместе с родительскими:


Резюме


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

Также, друзья, возможно вам было бы интересно почитать серию технических статей о нашем стеке или может быть что-то ещё?

P.S. Чуть более подробно узнать о преимуществах GraphQL для фронтенд разработчиков и о том, как мы используем GraphQL, можно на видео с выступления нашего бэкенд-разработчика Андрея Горинова

Комментарии (20)


  1. powerman
    18.02.2019 21:17
    +3

    Было бы любопытно услышать Ваше мнение по поводу моего старого комментария, ключевые моменты:


    корректно и полноценно реализовать GraphQL на сервере — крайне сложно. И я сейчас не про парсер GraphQL, который уже реализован в куче библиотек, а про бизнес-логику функций, которые получают/изменяют конкретные поля данных. В GraphQL слишком много от прямого доступа к данным, что сильно усложняет корректную реализацию бизнес-логики того, какие поля в каких ситуациях кому можно читать/писать. Так что требования к сложности, гибкости и разнообразию клиентских запросов должны быть поистине гигантскими (примерно масштабов фейсбука), чтобы вся эта сложность реализации GraphQL на бэкенде стала оправданной.
    Думаю, в будущем мы увидим очень много примеров дыр нового типа GraphQL-injection, когда через специальным образом сформированные GraphQL-запросы можно будет считывать и изменять данные, к которым доступа у этого юзера быть вообще не должно.


    1. andrei_anisimov Автор
      19.02.2019 06:34

      Да, действительно, все эти проблемы весьма актуальны. Реализация GraphQL бэкенда с гибким и секьюрным доступом к данным как-раз является одной из центральных проблем, которую мы решаем.

      С другой стороны, эти проблемы характерны не только GraphQL. Если учесть, что API множества более сложных приложений из-за нужд клиентов со временем из RESTful превращаются в JSON RPC с доморощенным протоколом передачи данных и описанными выше проблемами, которые разработчики каждый раз решают по-своему. В этом смысле GraphQL дает некий стандарт вокруг которого со временем возникнут средства, упрощающие реализацию подобных бэкендов с гибким доступом к данным. Другими словами, GraphQL — это не совершенно новый подход, а скорее попытка формализации существующих по факту, но разрозненных практик.

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


      1. powerman
        19.02.2019 07:08

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


        Но в большинстве других приложений количество разных запросов к данным конечно и даже не очень велико, они известны заранее, и не составляет большой проблемы реализовать для каждого отдельный вызов JSON RPC 2.0 (это стандарт которому уже почти 10 лет, не совсем понял что Вы имели в виду под "доморощенным протоколом") с простым и явным контролем доступа (плюс с любыми ручными оптимизациями/кешами для тяжёлых запросов). И я вот совсем не уверен, что GraphQL для таких приложений является подходящим решением — больше похоже на то, что он несёт с собой больше проблем и сложностей, чем решает.


        1. andrei_anisimov Автор
          19.02.2019 07:51

          С учетом сложности реализации самописного GraphQL-бэкенда я согласен что многим приложениям GraphQL не требуется. С точки зрения бэкенда безусловно всегда проще реализовать жесткий набор эндпоинтов, особенно когда набор клиентов ограничен и заранее известен.

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


    1. ogregor
      20.02.2019 09:33

      У Microsoft есть аналог ODATA, та же самая проблема. Если настроить модели не правильно, то можно одним запросом всю базу выкачать. В случае если приложение большое, то спроектировать эти модели на все юзкейсы становится практически невозможным.
      Поэтому как ODATA так и GraphQL можно рассматривать как дополнение а не замену.


  1. GHostly_FOX
    18.02.2019 22:13

    Сейчас мы переходим на GraphQL (чуть позже будет статья).
    Мы взяли GraphQL на вооружение для предоставление более гибкого интерфейса нашим REST API.
    Дало конечно больше удобства и весьма сокращает количество запросов к API на получение необходимой информации. Мутации (изменение данных) мы не используем т.к. наши API предназначены только для того, чтобы делится информацией и в нашем случае GraphQL дает нам делать это более Красиво )))


    1. andrei_anisimov Автор
      19.02.2019 06:42

      Согласен, несмотря на то, что у бэкенд разработчиков много весьма валидных вопросов по поводу реализации, большинство клиентских разработчиков, как вы сказали, отмечают удобство работы с GraphQL. А в конечном счете, как говорится, клиент всегда прав))


      1. powerman
        19.02.2019 07:14

        Это говорилось вовсе не про тех клиентов. :)


        И, кстати, лет 20 назад уже была технология, дающая клиентам почти то же самое, что GraphQL — называлась "клиент присылает серверу SQL-запрос (или отдельные элементы SQL-запроса)". Закончилось всё печально — несмотря на замечательную гибкость этого подхода для клиентов оказалось, что валидация на сервере не справляется с контролем безопасности, плюс некоторые запросы оказывались слишком "тяжёлыми", плюс изменение схемы БД требовало изменения клиентов. GraphQL, конечно, более продвинутый вариант, но не похоже, чтобы он решал все эти проблемы.


        1. andrei_anisimov Автор
          19.02.2019 08:02

          Согласен. Еще был/есть SOAP, у которого даже схема есть в спецификации. Но в этот раз все выглядит немного иначе. Основной фактор — общение на конференциях с разработчиками из таких компаний как Github, Airbnb, Jet.com, Atlassian и др, которые отмечают позитивный опыт в продакшене (у некоторых уже больше года) и повсеместные планы по расширению внедрения GraphQL. В конечном счете, с насыщением рынка API-продуктов, баланс сил смещается в сторону клиентских разработчиков, которые выбирают то API, которым им удобнее пользоваться, независимо от сложности реализации на бэке.


        1. GHostly_FOX
          20.02.2019 12:17

          Почему нет.
          Он принимает не SQL запрос, а структурированный по его правилам запрос.
          Шаг в лево, шаг в право — Ошибка.
          Преимущества над RestAPI:
          — он типизирован
          — он сам может рассказать какая у него структура причем сразу с описанием и указанием типов данных
          — он может рассказать какие поля получили флаг Deprecated
          — он не примет ни каких подзапросов SQL или Injection т.к. все данные будут проходить через модели и валидироваться на входе
          — он не раскрывает связей между объектами

          и так можно продолжать дальше.

          Принимать SQL — это не хорошо. Там слишком много лазеек которые можно использовать. GraphQL — уже лучше.


  1. ktretyak
    18.02.2019 22:18
    +1

    Есть ли понятие "версионности" в запросах на GraphQL, или эту надобность перекрывает указание нужной схемы от клиента? Тоесть, если клиент хочет некоторую версию данных от бекенда, то вместо номера версии он просто указывает нужную схему, или этого будет недостаточно?


    1. andrei_anisimov Автор
      19.02.2019 05:03

      GraphQL предлагает решение проблемы версионности путем добавления новых полей/запросов в схему. Поля, которые требуется исключить, сначала объявляются как @deprecated, давая клиентам знать что поле в будущем будет исключено из схемы. Подробнее об этом подходе: graphql.org/learn/best-practices/#versioning


  1. prostofilya
    19.02.2019 04:34

    А как там с кешированием?


    1. andrei_anisimov Автор
      19.02.2019 06:53

      В отличие от REST, где кэширование заложено на уровне протокола HTTP, в GraphQL оно остается на усмотрение разработчиков. Например Apollo Client имеет встроенный механизм кэширования запросов, который совмещен с управлением состоянием (state) приложения. На данном этапе мы не обнаружили серьезных недостатков связанных с кэшем, но не исключаем вероятность того что они возникнут в будущем и их прийдется решать.


      1. prostofilya
        19.02.2019 06:55

        Спасибо.


  1. tbicr
    19.02.2019 13:55

    Вот если нятягивать graphql на реляционную базу:

    как graphql помогает в решении делать join или подзапросы?
    как позволяет избежать большого количества join или избегает бамбёжки самыми толстыми запросами?
    как позволяет ограничить условие выборки и сортировки, чтобы попасть в индэкс?


    1. boblenin
      19.02.2019 20:41

      Все делается в резолверах. Если сделаете тупой резолвер — будет плохо с join-ами. Но вы всегда можете иметь несколько. Или постепенно переводить тупые резолверы на умные.

      По сути дела принятие решения перемещается из контроллера в резолвер. Вот и вся магия.


  1. boblenin
    19.02.2019 20:37
    +2

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

    В случае же махрового ентерпрайза, когда ваш потребитель выгребает практически весь ваш датасет, во весь рост вылезают проблемы масштабирования, отсутствие версионности, невнятная модель безопасности, отсутствие поддержки кэширования на уровне протокола. И решенния на GraphQL получаются чем-то вроде припудренной и весьма дряхлой примадонны SOAP (которой многие все еще пользуются — зона комфорта все-таки).

    И в общем дело не в том, что все что я перечислил не решаемо. Дело в том, что для решения всех этих проблем никогда нет ни времени, ни денег, ни управленческой воли. И в итоге применяя GraphQL мы усложняем жизнь клиентам (библиотеки все еще сырые, а в ручную писать все-таки труднее чем просто REST или RPC), а себе жизнь особо не облегчаем. Ну разве что даем разработчикам возможность еще один buzzword добавить в резюме.


    1. andrei_anisimov Автор
      20.02.2019 06:29

      Эта проблема действительно актуальна в контексте энтерпрайса. В нашем случае мы не пытаемся решить все ситуации за счет GraphQL и готовим к выпуску набор фич позволяющих прямой доступ к БД. Таким образом во многих проектах 50%+ API будет предоставлено платформой, а остальное можно точечно кастомизировать под конкретный случай. В этом плане GraphQL не диктует что обязательно должны быть CRUD-операции на все сущности со связями. Просто за счет строгой типизации и набора open-source средств с таким API намного приятнее работать на клиенте. При этом контроль эффективности запросов не уменьшается по сравнению с REST.

      SOAP действительно пытался решить многие подобные проблемы и в свое время звучал многообещающе, но XML, высокий порог вхождения, не такая развитая в то время open-source среда сделали работу с ним непривлекательной по сравнению с простым и элегантным REST+JSON. В случае с GraphQL работать с ним на наш взгляд действительно удобнее чем с REST, во многом именно за счет множества open-source средств. Такие вещи как статическая проверка типов, Apollo Client, автокомплит, средства типа GraphiQL и GraphQL Playground значительно ускоряют работу с данными на клиенте. После этого возвращение на REST вызывает негативные ощущения (субъективное мнение моe и команды, мы все делали REST/JSON RPC до 8base).


  1. Thisnickname2019
    20.02.2019 16:18

    Одним запросом всю базу, лихие ребята)) И да, у вас да и не только у вас, ни разу не Фейсбук.