Что такое GraphQL?

Zexuan Luo

Zexuan Luo

November 4, 2022

Technology

Что такое GraphQL? Насколько он популярен?

GraphQL — это язык манипулирования запросами, ориентированный на API, выпущенный Facebook в 2015 году. В отличие от других подходов к проектированию API, GraphQL позволяет клиентам формировать запросы на основе заранее согласованной структуры данных, а серверу — анализировать запрос и возвращать только необходимые данные. Таким образом, GraphQL обеспечивает богатство и гибкость, избегая потери производительности из-за избыточных данных, что делает его отличным выбором для приложений, работающих с множеством сложных объектов данных.

В 2018 году GraphQL получил полную спецификацию и стабильную версию. В том же году Facebook передал проект GraphQL в фонд GraphQL Foundation под эгидой Linux Foundation. С тех пор GraphQL нашел применение во многих открытых проектах и коммерческих организациях. На сегодняшний день на рынке существует несколько основных реализаций GraphQL для клиентской стороны. Серверные реализации доступны на всех основных языках программирования, а также на менее распространенных языках, таких как D и R.

Реальные сценарии и проблемы GraphQL

Наиболее известным примером использования GraphQL является GraphQL API GitHub.

До внедрения GraphQL GitHub предоставлял REST API для доступа к данным, генерируемым миллионами размещенных проектов. Этот API был настолько успешным, что стал образцом для подражания при проектировании REST API. Однако по мере роста числа объектов данных и увеличения их полей REST API начал проявлять все больше недостатков. На стороне сервера GitHub пришлось установить строгие ограничения на частоту вызовов, чтобы снизить затраты из-за объема данных, возвращаемых при каждом запросе. Разработчики, в свою очередь, столкнулись с этой проблемой: хотя один вызов возвращает много данных, большая их часть бесполезна. Чтобы получить конкретную информацию, разработчикам часто приходится выполнять несколько запросов, а затем писать много кода для объединения полезных данных из результатов запросов в нужный контент. В процессе они также вынуждены учитывать ограничения на количество вызовов.

Поэтому GitHub принял GraphQL сразу после его появления. GitHub стал "послом" GraphQL, представив его тысячам разработчиков. GraphQL API теперь является основным выбором для GitHub. С момента первого анонса поддержки GraphQL GitHub ежегодно публикует несколько статей о GraphQL. Чтобы помочь разработчикам перейти на GraphQL, GitHub создал интерактивное приложение для запросов: https://docs.github.com/en/graphql/overview/explorer. Разработчики могут изучать написание GraphQL с помощью этого приложения.

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

Как APISIX поддерживает GraphQL

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

Рассмотрим следующий GraphQL-запрос:

query getRepo { owner { name } repo { created } }

APISIX извлекает следующие три свойства GraphQL для маршрутизации:

  • graphql_operation
  • graphql_name
  • graphql_root_fields

В приведенном выше GraphQL-запросе:

  • graphql_operation соответствует query
  • graphql_name соответствует getRepo
  • graphql_root_fields соответствует ["owner", "repo"]

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

curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d ' { "methods": ["POST"], "uri": "/graphql", "vars": [ ["graphql_operation", "==", "query"], ["graphql_name", "==", "getRepo"], ["graphql_root_fields", "has", "owner"] ], "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:2022": 1 } } }'

Теперь выполним запрос с GraphQL-запросом:

curl -i -H 'content-type: application/graphql' \ -X POST http://127.0.0.1:9080/graphql -d ' query getRepo { owner { name } repo { created } }' HTTP/1.1 200 OK ...

Мы видим, что запрос достиг вышестоящего сервера, так как запрос соответствует всем трем условиям.

С другой стороны, если мы выполним запрос, который не соответствует условиям, например, без поля owner:

curl -i -H 'content-type: application/graphql' \ -X POST http://127.0.0.1:9080/graphql -d ' query getRepo { repo { created } }' HTTP/1.1 404 Not Found ...

Он не будет соответствовать правилу маршрутизации.

Мы можем создать еще один маршрут, который будет перенаправлять запросы без поля owner на другой вышестоящий сервер:

curl http://127.0.0.1:9180/apisix/admin/routes/2 \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d ' { "methods": ["POST"], "uri": "/graphql", "vars": [ ["graphql_operation", "==", "query"], ["graphql_name", "==", "getRepo"], ["graphql_root_fields", "!", "has", "owner"] ], "upstream": { "type": "roundrobin", "nodes": { "192.168.0.1:2022": 1 } } }'
curl -i -H 'content-type: application/graphql' \ -X POST http://127.0.0.1:9080/graphql -d ' query getRepo { repo { created } }' HTTP/1.1 200 OK ...

Перспективы поддержки GraphQL в APISIX

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

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

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

Например, у нас есть два API: GetEvent и GetLocation, которые возвращают типы Event и Location соответственно.

type Event { id: string location_id: string } type Location { id: string city: string } type Query { GetEvent(id: string): Event GetLocation(id: string): Location }

Мы можем добавить конфигурацию, которая объединяет эти два API в новый API под названием GetEventWithLocation, который выглядит так:

type EventWithLocation { id: string location: Location } type Query { GetEventWithLocation(id: string): EventWithLocation }

Конкретная реализация сшивания выполняется шлюзом. В приведенном выше примере шлюз разделяет API на два, вызывая GetEvent для получения location_id, а затем GetLocation для получения объединенных данных.

Короче говоря, преобразуя REST в GraphQL, каждый REST API можно превратить в соответствующую модель GraphQL; а с помощью сшивания схем несколько моделей можно объединить в один GraphQL API. Таким образом, мы можем построить богатый и гибкий GraphQL API поверх существующего REST API и управлять конкретными плагинами на уровне REST API. Этот дизайн также решает некоторые проблемы оркестрации API. Как в примере выше, мы берем выходные данные одного API (Event.location_id) в качестве входных данных для другого API (Location.id).

Tags: