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.

  1. Запустите команду dotnet new web GraphQL-Intro. Вы можете заменить часть GraphQL-Intro на любое имя, которое хотели бы дать своему проекту.

  2. Запустите команду dotnet add package HotChocolate.AspNetCore, чтобы установить NuGet-пакет необходимый для интеграции Hot Chocolate в ASP.NET Core.

  3. Запустите команду dotnet add package HotChocolate.AspNetCore.Playground, чтобы добавить пакет GraphQL Playground.

  4. Запустите команду dotnet add package Microsoft.EntityFrameworkCore.InMemory, чтобы добавить in-memory поставщика для Entity Framework.

После выполнения всех вышеперечисленных действий у вас будет готовый к работе ASP.NET Core проект со всеми зависимостями, необходимыми для настройки сервера GraphQL и обслуживания API. Инструкции для добавления NuGet-пакетов, приведенные выше, подходят для командной строки dotnet. Если вы используете Visual Studio и хотите использовать консоль NuGet Package Manager, то вам нужно будет выполнить команды, перечисленные на рисунке 1.

Рисунок 1: Необходимые NuGet-пакеты 
Рисунок 1: Необходимые NuGet-пакеты 

Определение схемы

В качестве примера мы будем создавать 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, но он должен быть исправлен ​​в ближайшее время. Он появляется, потому что директива, которую вы видели, регистрируется автоматически, а из-за этого появляется пользовательский скалярный тип.

Рисунок 2: Схема GraphQL
Рисунок 2: Схема GraphQL

Кликните на кнопку Schema еще раз, чтобы закрыть это окно. Перейдите в текстовую область на левой панели и введите в нее запрос, как показано ниже. Кликните на кнопку Play для запуска мутации. В результате вы должны получить успешный ответ с сохраненными данными, как вы можете видеть на рисунке 3.

Рисунок 3: Результат GraphQL-мутации
Рисунок 3: Результат GraphQL-мутации
mutation {
  book(title: "Shape Up", chapters: 4, pages: 100, author: "Anchor Duchito")
  {
    id
    chapters
    title 
    author {
      name
    }
  }  
}

Вы можете изменить запрос мутации, чтобы добавить другую книгу. Затем скопируйте и выполните приведенный ниже запрос, чтобы получить все книги:

{
  books{
    title
    author {
      name
    }
  }
}

В результате вы должны получить ответ, аналогичный тому, что вы видите на рисунке 4.

Рисунок 4: Результат GraphQL-запроса
Рисунок 4: Результат GraphQL-запроса

Заключение

Сегодня я познакомил вас с мутациями в 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 разработчик».

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