GraphQL представляет из себя язык запросов и манипулирования данными для API, а также среду для выполнения этих запросов с существующими данными. Он позволяет различным клиентам использовать ваш API и запрашивать только те данные, которые им нужны, а также он помогает решить проблемы, которые есть у некоторых REST-сервисов, такие как избыток (over-fetching) и недостаток (under-fetching) данных. В моей статье «Введение в GraphQL для .NET-разработчиков: схема, резолвер и язык запросов», я уже рассказал о системе типов GraphQL, языке запросов, схеме и резолвере. Я показал вам, как создать сервер GraphQL, используя библиотеку GraphQL .NET, и протестировал API, сформировав несколько запросов из интерактивной среды (playground) GraphQL. В этой же статье я расскажу вам о мутациях в GraphQL. Я также отойду от использования статического метода, который я показывал в вышеупомянутой статье, и буду использовать Entity Framework (с in-memory поставщиком) для доступа и хранения данных.
В прошлой статье для создания сервера GraphQL в ASP.NET Core я использовал библиотеку GraphQL .NET. В этой статье я буду использовать библиотеку Hot Chocolate. Многие GraphQL API, созданные на основе .NET, в настоящее время работают с использованием GraphQL .NET, однако GraphQL .NET больше не мейнтейнится, и по этому я не рекомендуется продолжать использовать ее для новых приложений. Библиотека Hot Chocolate наоборот находится в активной разработке и радует нас регулярными релизами. Кроме того, у них есть активный воркспейс в Slack, где вы можете задать свои вопросы. Вот почему в этой статье я буду использовать именно ее.
Создание проекта
Для начала вам нужно создать ASP.NET Core приложение, где будет размещаться сервер GraphQL. Откройте Visual Studio или JetBrains Rider и создайте новый ASP.NET Core проект на основе шаблона Empty или откройте приложение командной строки и следуйте приведенным ниже инструкциям, если вы используете VS Code.
Запустите команду
dotnet new web GraphQL-Intro
. Вы можете заменить часть GraphQL-Intro на любое имя, которое хотели бы дать своему проекту.Запустите команду
dotnet add package HotChocolate.AspNetCore
, чтобы установить NuGet-пакет необходимый для интеграции Hot Chocolate в ASP.NET Core.Запустите команду
dotnet add package HotChocolate.AspNetCore.Playground
, чтобы добавить пакет GraphQL Playground.Запустите команду
dotnet add package Microsoft.EntityFrameworkCore.InMemory
, чтобы добавить in-memory поставщика для Entity Framework.
После выполнения всех вышеперечисленных действий у вас будет готовый к работе ASP.NET Core проект со всеми зависимостями, необходимыми для настройки сервера GraphQL и обслуживания API. Инструкции для добавления NuGet-пакетов, приведенные выше, подходят для командной строки dotnet. Если вы используете Visual Studio и хотите использовать консоль NuGet Package Manager, то вам нужно будет выполнить команды, перечисленные на рисунке 1.
Определение схемы
В качестве примера мы будем создавать API, который позволит пользователям запрашивать книги и их авторов. Вы можете представить, что вы создаете этот API для небольшой издательской компании. Как вы, возможно, уже знаете, GraphQL API строго типизирован, и поэтому вам необходимо заранее определить типы для представления того, какие данные можно получать из API. Поскольку вы создаете API для книг и авторов, в вашей схеме должны быть типы Book
и Author
. Таким образом проектировать схемы вы будете из кода, и для этого вы определите POCO-типы (“plain old CLR object”), которые представляют эти типы в схеме.
Откройте проект в текстовом редакторе или IDE по вашему выбору и создайте новый файл класса Book.cs
. Добавьте в него приведенный ниже фрагмент кода:
using HotChocolate;
public class Book
{
public int Id { get; set; }
[GraphQLNonNullType]
public string Title { get; set; }
public int Pages { get; set; }
public int Chapters { get; set; }
[GraphQLNonNullType]
public Author Author { get; set; }
}
Создайте еще один новый файл класса Автор.cs
и добавьте в него код, приведенный ниже:
using HotChocolate;
using HotChocolate.Types;
public class Author
{
[GraphQLType(typeof(NonNullType<IdType>))]
public int Id { get; set; }
[GraphQLNonNullType]
public string Name { get; set; }
}
Обратите внимание на атрибуты [GraphQLNonNullType]
и [Тип GraphQL]
во фрагментах кода, которые вы только что добавили. Это атрибуты из библиотеки Hot Chocolate, и вы использовали их для указания типа некоторых полей. Атрибут [GraphQLNonNullType]
нужен для того, чтобы указать, что поле не может быть пустым. Например, вы использовали его, чтобы указать, что поле title типа Book не может быть нулевым. Атрибут [GraphQLType]
используется для указания типа поля. В этом коде он нужен нам для указания того, что Author Id
является необнуляемым типом ID
GraphQL.
Операции Mutation
Мутация GraphQL — это тип операции, которая позволяет клиентам изменять данные на сервере. Именно с помощью данного типа операций вы можете добавлять, удалять и обновлять данные на сервере. Для чтения данных в GraphQL используется операции типа запроса, о которой я рассказывал в статье “Введение в GraphQL для .NET-разработчиков: схема, резолвер и язык запросов” (CODE Magazine, сентябрь 2019 г.). GraphQL имеет три основных типа, которые определяют операции сервера GraphQL:
Тип запроса (Query)
Тип мутации (Mutation)
Тип подписки (Subscription)
Обязательный минимум, который должен определять сервер GraphQL, — это тип Query
; используемый вами движок GraphQL проверяет схему, чтобы убедиться, что это требование выполнено.
Хорошо. Допустим, вам нужно создать мутацию, позволяющую пользователям добавлять в приложение новую книгу. Это означает, что вам необходимо определить тип Mutation
.
Создайте новый файл Mutation.cs
и добавьте туда код из листинга 1. Код в листинге 1 определяет класс с методом под названием Book()
, который принимает некоторые параметры. Имя функции будет использоваться как имя поля в схеме. Первый параметр метода — это объект Entity Framework DbContext, помеченный атрибутом [Service]
. Атрибут [Service]
из библиотеки Hot Chocolate. Hot Chocolate поддерживает внедрение зависимостей через метод класса, и атрибут [Service]
используется для обозначения того, что данный параметр должен быть внедрен. Остальные параметры представляют аргументы для этого поля. Остальной код в методе представляет из себя операторы для сохранения новой книги в базе данных с помощью Entity Framework.
Листинг 1: Объявление типа Mutation и его резолвера
using System.Threading.Tasks;
using HotChocolate;
using HotChocolate.Types;
namespace GraphQL_Intro
{
public class Mutation
{
public async Task<Book> Book ([Service] BookDbContext dbContext, string title, int pages, string author, int chapters)
{
var book = new Book
{
Title = title,
Chapters = chapters,
Pages = pages,
Author = new Author { Name = author }
};
dbContext.Books.Add(book);
await dbContext.SaveChangesAsync();
return book;
}
}
}
Определение операций Query
Отлично, вы определили тип мутации, которому и посвящена эта статья. Однако у вас еще нет типа запроса. Вам нужно определить тип запроса, чтобы пользователи могли запрашивать сохраненные книги.
Создайте новый файл Query.cs
, а затем добавьте туда код из листинга 2. Код содержит два метода GetBooks()
и GetBook()
. Эти методы являются функциями-резолверами и будут сопоставлены с полями books()
и book(ID id)
в схеме. По соглашению Hot Chocolate удаляет слово “Get” из имени метода и использует оставшуюся часть его названия в качестве имени поля. BookDbContext
будет внедряться как сервис и использоваться для извлечения данных.
Листинг 2: Объявление типа Query и его резолверов
using System.Collections.Generic;
using System.Linq;
using HotChocolate;
using Microsoft.EntityFrameworkCore;
public class Query
{
[GraphQLNonNullType]
public List<Book> GetBooks ([Service] BookDbContext dbContext) => dbContext.Books.Include(x => x.Author).ToList();
// По соглашению GetBook() будет объявлен как book() в типе запроса.
public Book GetBook([Service] BookDbContext dbContext, int id) => dbContext.Books.FirstOrDefault(x => x.Id == id);
}
Настройка middleware GraphQL
На данный момент у вас уже есть код, определяющий схему GraphQL и ее резолверы. Следующее, что вам нужно сделать, это настроить GraphQL ASP.NET Core middleware. Вы также нужно добавить код для класса BookDbContext
, который вы использовали выше.
Создайте новый файл BookDbContext.cs
, который будет содержать код класса BookDbContext
. Скопируйте приведенный ниже код и вставьте его в файл BookDbContext.cs
, который был добавлен в соответствии с инструкциями в этом абзаце.
using Microsoft.EntityFrameworkCore;
public class BookDbContext : DbContext
{
public BookDbContext(DbContextOptions<BookDbContext> options) : base(options)
{
}
public DbSet<Book> Books { get; set; }
}
Откройте Startup.cs
и добавьте следующие Using-операторы в этот файл:
using HotChocolate;
using HotChocolate.AspNetCore;
using Microsoft.EntityFrameworkCore;
Перейти к методу ConfigureServices()
и добавьте в него следующий код:
services.AddDbContext<BookDbContext>(options => options.UseInMemoryDatabase(databaseName: "Books" ));
services.AddGraphQL(SchemaBuilder.New().AddQueryType<Query>().AddMutationType<Mutation>().Create());
Этот код регистрирует типы GraphQL в схеме GraphQL. Он также регистрирует Entity Framework DbContext для использования поставщика in-memory базы данных.
Теперь, когда сервисы зарегистрированы, вам нужно добавить middleware GraphQL в конвейер, чтобы сервер мог обслуживать GraphQL-запросы. Добавьте приведенный ниже код в метод Configure()
:
app.UseGraphQL("/graphql").UsePlayground("/graphql");
Метод UseGraphQL()
настраивает middleware GraphQL и устанавливает путь для GraphQL API на /graphql
. Вы также можете настроить интерактивную среду GraphQL с помощью метода UsePlayground()
.
На этом этапе приложение готово к обработке GraphQL-запросов. Теперь мы протестируем наше приложение, запустив его и отправив несколько запросов через GraphQL Playground.
Тестируем приложение
Чтобы протестировать приложение, вам нужно запустить его и перейти в GraphQL Playground. Откройте командную строку и выполните команду dotnet run
или нажмите F5 (или Ctrl+F5) в Visual Studio или JetBrains Rider, чтобы запустить приложение. Откройте браузер и перейдите по адресу https://localhost:5001/graphql/playground. Откроется пользовательский интерфейс GraphQL Playground, из которого вы и будете писать свои GraphQL-запросы.
Кликните на кнопку с надписью Schema
в правой части страницы, после чего вы должны увидеть схему GraphQL, отображаемую в SDL, точно так же, как продемонстрировано на рисунке 2. Вы заметите пользовательский скалярный тип и директиву, объявленную в схеме. Это баг в Hot Chocolate, но он должен быть исправлен в ближайшее время. Он появляется, потому что директива, которую вы видели, регистрируется автоматически, а из-за этого появляется пользовательский скалярный тип.
Кликните на кнопку Schema еще раз, чтобы закрыть это окно. Перейдите в текстовую область на левой панели и введите в нее запрос, как показано ниже. Кликните на кнопку Play для запуска мутации. В результате вы должны получить успешный ответ с сохраненными данными, как вы можете видеть на рисунке 3.
mutation {
book(title: "Shape Up", chapters: 4, pages: 100, author: "Anchor Duchito")
{
id
chapters
title
author {
name
}
}
}
Вы можете изменить запрос мутации, чтобы добавить другую книгу. Затем скопируйте и выполните приведенный ниже запрос, чтобы получить все книги:
{
books{
title
author {
name
}
}
}
В результате вы должны получить ответ, аналогичный тому, что вы видите на рисунке 4.
Заключение
Сегодня я познакомил вас с мутациями в GraphQL – одним из трех основных типов операций в GraphQL. Вы также создали схему из кода. Я показал вам некоторые атрибуты, которые можно использовать для настройки схемы, а также способы реализации резолверов. Вы использовали поставщика in-memory базы данных Entity Framework In-Memory в качестве источника данных и узнали, как настроить middleware GraphQL.
Вы узнали, как создать GraphQL API с типами Mutation и Query. Этих навыков вам должно хватить для создания GraphQL API, выполняющего CRUD-операции. Теперь вы также можете похвастаться перед друзьями тем, что вы GraphQL-разработчик. Чтобы доказать им это, я хочу, чтобы вы добавили в API следующий набор функций:
Добавьте запрос для поиска авторов по имени.
Разрешите книгам иметь издателей. Это подразумевает, что вы добавите новый тип в схему. Вы должны иметь возможность независимо добавлять издателей и запрашивать все книги, принадлежащие конкретному издателю.
Если вы на чем-нибудь застрянете или захотите, чтобы я взглянул на ваше решение, не стесняйтесь писать мне в личные сообщения в Твиттере. В Твиттере я @p_mbanugo.
Хотя эти навыки уже делают вас гордым разработчиком GraphQL, я не собираюсь останавливаться на достигнутом. В моей следующей статье я собираюсь рассказать вам о паттерне DataLoader и о том, как он влияет на производительность приложения GraphQL. Оставайтесь с нами и сохраняйте энтузиазм!
Вы можете найти весь код для этого поста на GitHub по адресу https://github.com/pmbanugo/graphql-intro-aspnet-core. Клонируйте репозиторий и переходите на ветку mutation.
Приглашаем всех желающих на открытое занятие «Принцип работы «Сборщика Мусора» в .NET», на котором мы:
— разберемся с тем, как хранятся объекты в памяти в .NET;
— познакомимся с принципом выделения физической памяти для приложений;
— поймем принцип работы сборщика мусора (поколения, стратегии, карточный стол);
— начнем отличать деструкторы от финализаторов и научимся использовать Disposable Pattern.
Регистрация на урок открыта на странице онлайн-курса «C# ASP.NET Core разработчик».