Найти в Дзене

ZeroQL — типобезопасный производительный GraphQL-клиент для .NET

Оглавление

GraphQL — это уже не модно, а популярно. И он продолжает становиться всё более популярной технологией для построения веб-серверов.

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

Ну, то есть, есть что-то. Например, Strawberry Shake требует, чтобы вы написали сырой GraphQL, но создает все необходимые обертки для С# — так что в итоге получается типобезопасный API. Но хочется чего-то более нативного, эдакое обобщенное АПИ чтобы можно было задавать запросы подобным образом:

ZeroQL

ZeroQL — это клиент GraphQL на языке C# с Linq-подобным интерфейсом и отличной производительностью, эквивалентной простому HTTP-вызову.

Давайте посмотрим, как он работает. Пусть у нас есть GraphQL-сервер на адресе http://localhost:10000/ с следующей схемой данных:

-2

Для начала создадим тестоый проект и настроим его для работы с нашим сервером — для этого нужно подключить нагет-пакет ZeroQL, получить схему с сервера GraphQL, установить утилиту ZeroQL.CLI и сгенерировать с её помощью типизированные обертки для схемы:

-3

Для проекта в активной разработке можно добавить шаги обновления схемы и генерации оберток как часть процесса сборки проекта.

-4

Отлично. Сразу после этого можно отправить первый запрос на GraphQL-сервер:

-5

Как это работает

Класс MyServerGraphQLClient создается с помощью ZeroQL.CLI. В нем есть метод Query, который принимает делегат Func<TQuery, TResult> query, нужные типы запросов и результатов для делегата генерируются по схеме GraphQL-сервера.

Source generator ищет использования метода Query, анализирует запрос в делегате, преобразует его в соответствующий GraphQL и складывает соответствия делагата (в виде строки) и GraphQL-запроса в словарь.

Чтобы достать значение запроса в виде строки в методе Query используется скрытый аргумент queryKey с атрибутом CallerArgumentExpression:

-6

Атрибут CallerArgumentExpressionAttribute появился в C# 10. Он позволяет нам получить строковое представление выражения, которое было передано внутри аргумента. В нашем случае мы ищем аргумент query, который равен static q => q.Me(o => new { o.Id, o.FirstName, o.LastName }), в результате аргумент queryKey будет содержать строку "static q => q.Me(o => new { o.Id, o.FirstName, o.LastName })".

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

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

-7

, то это самый простой способ убедиться, что все параметры учтены — source generator сереализует их и добавит в запрос.

Поддерживаемые фичи

Во-первых, вложенные поля!

-8

Во-вторых, возврат нескольких значений из разных запросов:

-9

Именованные запросы:

-10

Мутации данных:

-11

Ограничения

Из-за атрибута CallerArgumentExpressionAttribute библиотека доступна только для .NET 6. Сейчас в разработке находятся:

  • фрагменты
  • подписки
  • @defer
  • @stream

Тут стоит сказать, что библиотека очень новая и находится в активной разработке. Можно порадовать автора звездой на Гитхабе.

Оригинальный анонс на dev.to.