![](https://habrastorage.org/webt/kd/5b/wk/kd5bwk8vnic5c8ahn4v5qzsxa-8.png)
Введение:
Все чаще слышу об использовании GraphQL, но так как последнее время все рабочее время уходило на Xamarin + GRPC + ASP.NET Core, технология казалась для меня загадочно манящей и очень не известной, потому решил опробовать ее на нескольких языках, чтобы увидеть какие плюсы и минусы в разных подходах. Начать решил с родного и любимого C# + Hot Chocolate, но планирую так же попробовать Typescript + Prisma, Kotlin + graphql-kotlin, и возможно что-то другое.
Что будем делать:
Посмотрев несколько обзорных видео и почитав доклады, очень заинтересовала федерация схем и единый шлюз для сервисов, потому в статье я опишу создание вымышленного сервиса, который будет предоставлять доступ к информации о какой-нибудь киновселенной, и создам шлюз на Hot Chocolate, из которого будет доступен этот сервис. В следующих статьях, сервисы на других языках будут подключаться к этому шлюзу, если не найдется лучшей альтернативы, на языке из статьи “спойлер”(найдется, Hot Chocolate хорош, но в других языках поддержка graphQL реализована интереснее)
Подготовка проекта:
Я буду использовать для проекта Rider + .Net Core 6 + Postgres в Docker, и первой киновселенной у нас будет вселенная Джеймса Бонда, создаем свежий солюшен и добавляем туда первый проект.
Добавляем проект
![](https://habrastorage.org/webt/bj/lf/av/bjlfavwwxrivuxp6ztoacbwiseo.png)
![](https://habrastorage.org/webt/bj/lf/av/bjlfavwwxrivuxp6ztoacbwiseo.png)
Требуемые nuget-пакеты
![](https://habrastorage.org/webt/r3/h6/ua/r3h6uaa1ddawn1ej3zoeoiwpesu.png)
![](https://habrastorage.org/webt/r3/h6/ua/r3h6uaa1ddawn1ej3zoeoiwpesu.png)
Теперь создадим 3 сущности:
- Бонд
- Режиссер
- Фильм
Сущности
![](https://habrastorage.org/webt/4w/vo/mh/4wvomhdasmy-a7pa7sntqvklzcc.png)
![](https://habrastorage.org/webt/4w/vo/mh/4wvomhdasmy-a7pa7sntqvklzcc.png)
Настроим DbContext, понимаю, что это будет выглядеть не эстетично с точки зрения нейминга, стараюсь настраивать модели правильно, но сейчас статья не совсем об этом:)
DbContext
![](https://habrastorage.org/webt/1j/ey/np/1jeynpqh1o9mixelzjess03nrr4.png)
![](https://habrastorage.org/webt/1j/ey/np/1jeynpqh1o9mixelzjess03nrr4.png)
Прописываем строку для подключения, создаем и выполняем миграцию
![](https://habrastorage.org/webt/lz/sh/1s/lzsh1sx4gq_3ozhe7y5rif0s_oo.png)
![](https://habrastorage.org/webt/mr/fy/c8/mrfyc8ufyfyol_rfcbbuxhslonc.png)
![](https://habrastorage.org/webt/lz/sh/1s/lzsh1sx4gq_3ozhe7y5rif0s_oo.png)
![](https://habrastorage.org/webt/mr/fy/c8/mrfyc8ufyfyol_rfcbbuxhslonc.png)
Так выглядит структура нашей базы
![](https://habrastorage.org/webt/jg/cy/re/jgcyre9x0kdhrywh3zntwt6klts.png)
![](https://habrastorage.org/webt/jg/cy/re/jgcyre9x0kdhrywh3zntwt6klts.png)
Hot Chocolate:
Возможно вам уже известно, но на всякий случай расскажу еще раз, в GraphQL есть 3 основных блока:
- Queries(запросы) — используются непосредственно для получения данных, и могут быть ассоциированы с буквой R из всем известного CRUD
- Mutations(мутации) — используются для создания, изменения и удаления данных остальные буквы из всем известного CRUD
- Subscriptions(подписки) — это можно сказать события, при изменении, каких-то данных вы можете сообщить об этом своим подписчикам
Начнем с Query:
В Program.cs добавляем сервис “GraphQLServer” и добавляем созданный нами Query-класс.
Также давайте сразу “замаппим” GraphQL, по умолчанию в Hot Chocolate маппинг происходит на адрес “/graphql”, но мне больше нравится путь “/api”.
Добавляем поддержку Query
![](https://habrastorage.org/webt/rg/zg/go/rgzggodbiu8rqez5tlo0tfrno6s.png)
![](https://habrastorage.org/webt/rg/zg/go/rgzggodbiu8rqez5tlo0tfrno6s.png)
Теперь давайте перейдем в класс Query, и добавим щепотку магии:)
Мы добавляем сущности которые мы хотим получить, а также добавим комментарии, и включим их генерации в проекте.
Это самая простая реализация запросов для GraphQL
![](https://habrastorage.org/webt/sn/gq/zp/sngqzppwdy0lpe5rabsssata2k0.png)
![](https://habrastorage.org/webt/sn/gq/zp/sngqzppwdy0lpe5rabsssata2k0.png)
Если сейчас запустить наш сервис, и перейти, по данному url, мы увидим, что у нас появились все наши запросы, а также документация, которая была взята из комментариев в коде.
Так выглядят наши запросы GraphQL
![](https://habrastorage.org/webt/v2/bi/ug/v2biugk0ldm5afutpsyewleowv8.png)
![](https://habrastorage.org/webt/v2/bi/ug/v2biugk0ldm5afutpsyewleowv8.png)
Но если сейчас выполнить запрос, то он будет максимально скучный, мы можем выбрать только всех обитателей одной из таблиц, но не можем фильтровать или еще как-то ограничивать данные, но Hot Chocolate позволяет нам также добавить и это достаточно простым способом, в волшебном пакете HotChocolate.Data, есть несколько атрибутов которые позволяют нам не писать вручную фильтрации, сортировки и прочее, а просто пометив запрос одним из атрибутов, добавить эти возможности, атрибутов множество, но я добавлю и расскажу только о нескольких из них.
Вешаем атрибуты на требуемый метод:
![](https://habrastorage.org/webt/ff/ve/vl/ffvevlh5ehct8fqbv5aguy_3_lm.png)
![](https://habrastorage.org/webt/ff/ve/vl/ffvevlh5ehct8fqbv5aguy_3_lm.png)
Говорим GraphQL, что мы используем эти возможности
![](https://habrastorage.org/webt/8c/7v/7u/8c7v7uzvenuse4mjq0xb4bhybq4.png)
![](https://habrastorage.org/webt/8c/7v/7u/8c7v7uzvenuse4mjq0xb4bhybq4.png)
Что же делают эти атрибуты:
-
[UseFiltering]
Добавляет возможность благодаря where фильтровать запрос практически как душе угодно. -
[UseSorting]
Добавляет возможность сортировки данных в вашем запросе. -
[UseProjection]
Добавляет оптимизацию запроса, чтобы сгенерированный SQL не тащил из базы данных все подряд, а только требуемые данные.
Если сейчас открыть запустить наш API, то мы увидим, что появились новые возможности:
![](https://habrastorage.org/webt/bz/ir/jd/bzirjdn73cmb_raqmnmq0gdfces.png)
![](https://habrastorage.org/webt/bz/ir/jd/bzirjdn73cmb_raqmnmq0gdfces.png)
И для примера такой запрос GraphQL преобразуется в такой запрос SQL
![](https://habrastorage.org/webt/x1/3o/_k/x13o_kz9kypxevv_moxt9638uk0.png)
![](https://habrastorage.org/webt/lu/0s/uh/lu0suheu0yw1eieqzlqnrd0v0k8.png)
![](https://habrastorage.org/webt/x1/3o/_k/x13o_kz9kypxevv_moxt9638uk0.png)
![](https://habrastorage.org/webt/lu/0s/uh/lu0suheu0yw1eieqzlqnrd0v0k8.png)
На этом закончим с Query и перейдем к Mutation
Mutation:
добавляем и регистрируем класс Mutation, как мы делали с Query
![](https://habrastorage.org/webt/0s/pm/uf/0spmuffrmgsnbiqmupoue3wh_24.png)
![](https://habrastorage.org/webt/0s/pm/uf/0spmuffrmgsnbiqmupoue3wh_24.png)
C мутациями к сожалению, в hot chocolate чуть посложнее, чем с запросами, потому придется написать чуть больше кода, код будет минимально возможный для операций, потому сильно не пинайте если не понравится, мне он тоже не нравится:) Но сейчас мы говорим не о EF
Базовый код для Mutation
![](https://habrastorage.org/webt/m0/ir/jy/m0irjywb86469pmj9lsfthfiafa.png)
![](https://habrastorage.org/webt/m0/ir/jy/m0irjywb86469pmj9lsfthfiafa.png)
Если теперь запустить наш проект, мы увидим добавленные нами мутации:
![](https://habrastorage.org/webt/yt/nu/0g/ytnu0gzphios5yyobwofgmhyzxc.png)
![](https://habrastorage.org/webt/yt/nu/0g/ytnu0gzphios5yyobwofgmhyzxc.png)
А теперь последнее в нашем списке, это подписки. С подписками все так же просто как и с остальным:)
Subscriptions:
С подписками повторяем наш путь из прошлых реализаций, добавляем класс Subscription, регистрируем его, и для нашего примера добавим операции с подписками в памяти, также нам нужно указать использование WebSockets:
Обновления в Program.cs
![](https://habrastorage.org/webt/c2/xv/mq/c2xvmqfnxvuzclm4f5lwvnp_tmq.png)
![](https://habrastorage.org/webt/c2/xv/mq/c2xvmqfnxvuzclm4f5lwvnp_tmq.png)
Теперь давайте добавим подписку на добавление нового Джеймса Бонда,
для этого нам нужен атрибут Subscribe, который как раз говорит нам о том, что это подписка, и в параметрах нам нужен атрибут EventMessage, который сообщает нам о типе сообщения из события.
Наша новая подписка
![](https://habrastorage.org/webt/_2/ml/hy/_2mlhyujmnzajtzygieq0i50-p0.png)
![](https://habrastorage.org/webt/_2/ml/hy/_2mlhyujmnzajtzygieq0i50-p0.png)
Ну и осталось самое малое, в методе, который должен нам сообщать о событии, нужно добавить его отправку.
Для этого добавим в параметры этого метода
ITopicEventSender sender
, пометив его атрибутом [Service]
, и вызовем в нем отправку, передав имя нашего события, и требуемый параметр:
Наша новая подписка
![](https://habrastorage.org/webt/cd/fq/jk/cdfqjkcugzvmv4muewrm5fwzbla.png)
![](https://habrastorage.org/webt/cd/fq/jk/cdfqjkcugzvmv4muewrm5fwzbla.png)
Давайте запустим, и убедимся что подписка появилась:
![](https://habrastorage.org/webt/jm/au/zu/jmauzunxlhjcwk7v1higsq8tywa.png)
![](https://habrastorage.org/webt/jm/au/zu/jmauzunxlhjcwk7v1higsq8tywa.png)
Gateway:
Tеперь давайте добавим шлюз, чтобы можно было из одной точки получить доступ ко всем нашим сервисам, добавляем новый проект, c такими же параметрами как и у прошлого:
Создаем новый проект в том же решении
![](https://habrastorage.org/webt/8b/kn/fa/8bknfajpsxqi50vybm2cjng9lvs.png)
![](https://habrastorage.org/webt/8b/kn/fa/8bknfajpsxqi50vybm2cjng9lvs.png)
В этот проект добавляем пару nuget-пакетов
![](https://habrastorage.org/webt/l2/bq/cp/l2bqcpd9anbtsdsxeaxarqccnbs.png)
![](https://habrastorage.org/webt/l2/bq/cp/l2bqcpd9anbtsdsxeaxarqccnbs.png)
И написать совсем немного кода, на данном этапе будет вообще одностраничник, мы добавляем новый HttpClient, указываем его имя (вынес в константы) и адрес, и потом после добавления GraphQLServer добавляем удаленную схему также указывая ее:
Изменения в Program.cs
![](https://habrastorage.org/webt/rl/ab/c_/rlabc_jdsm5ld1xxu-f_q3nblbu.png)
![](https://habrastorage.org/webt/rl/ab/c_/rlabc_jdsm5ld1xxu-f_q3nblbu.png)
Понимаю, что данная статья к сожалению не интерактивна, потому тем кому интересно оставляю ссылку на свой GitHub, где буду публиковать улучшения этого проекта, а так же GraphQL примеры на других языках: