Введение в GraphQL и C#
GraphQL — это язык запросов для API, который предоставляет клиентам возможность запрашивать только те данные, которые им действительно нужны. В отличие от REST, где структура ответа фиксирована, GraphQL позволяет гибко формировать запросы и получать ровно то, что требуется. В этом туториале мы разберём, как создать полноценный GraphQL API на платформе C# (.NET) с использованием библиотеки Hot Chocolate.
Этот материал будет полезен как новичкам, так и опытным разработчикам, которые хотят освоить современный подход к построению API. Мы пройдём все этапы: от настройки проекта до реализации запросов и мутаций. К концу статьи у вас будет работающий GraphQL-сервер, готовый к интеграции с любым клиентом.
Установка и настройка проекта
Первым делом нам нужно создать новый проект ASP.NET Core. Откройте терминал или командную строку и выполните команду:
dotnet new web -n GraphQLDemo
cd GraphQLDemo
Теперь добавим пакет Hot Chocolate — это основная библиотека для работы с GraphQL в .NET:
dotnet add package HotChocolate.AspNetCore
После установки откройте файл Program.cs и настройте сервисы. Вот минимальная конфигурация для запуска GraphQL:
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Добавляем GraphQL сервисы
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>();
var app = builder.Build();
// Подключаем middleware для GraphQL
app.MapGraphQL();
app.Run();
Обратите внимание: мы ещё не создали класс Query. Это наш корневой тип, который определяет доступные запросы. Создадим его в следующем разделе.
Определение схемы: типы и резолверы
Создание модели данных
Для начала определим простую модель данных. Пусть это будет книга:
public class Book
{
public string Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public int Year { get; set; }
}
Создание Query-класса
Теперь создадим класс Query, который будет возвращать данные. В Hot Chocolate резолверы — это обычные методы, которые могут быть синхронными или асинхронными:
public class Query
{
private static readonly List<Book> Books = new()
{
new Book { Id = "1", Title = "Война и мир", Author = "Лев Толстой", Year = 1869 },
new Book { Id = "2", Title = "Преступление и наказание", Author = "Фёдор Достоевский", Year = 1866 }
};
public IQueryable<Book> GetBooks() => Books.AsQueryable();
public Book? GetBook(string id) => Books.FirstOrDefault(b => b.Id == id);
}
Метод GetBooks возвращает список всех книг, а GetBook — одну книгу по идентификатору. Hot Chocolate автоматически преобразует эти методы в поля GraphQL-схемы.
Настройка типов (опционально)
Иногда нужно явно указать, какие поля должны быть доступны. Для этого можно использовать атрибуты или Fluent API. Пример с атрибутами:
public class Book
{
[GraphQLName("id")]
public string Id { get; set; }
[GraphQLNonNullType]
public string Title { get; set; }
public string Author { get; set; }
public int Year { get; set; }
}
Теперь запустите проект командой dotnet run и перейдите в браузере по адресу http://localhost:5000/graphql. Вы увидите встроенную Banana Cake Pop — среду для тестирования запросов.
Реализация мутаций (создание, обновление, удаление)
Мутации в GraphQL используются для изменения данных. Создадим класс Mutation и добавим методы для работы с книгами.
public class Mutation
{
private static readonly List<Book> Books = new()
{
new Book { Id = "1", Title = "Война и мир", Author = "Лев Толстой", Year = 1869 }
};
public Book AddBook(string title, string author, int year)
{
var book = new Book
{
Id = Guid.NewGuid().ToString(),
Title = title,
Author = author,
Year = year
};
Books.Add(book);
return book;
}
public bool DeleteBook(string id)
{
var book = Books.FirstOrDefault(b => b.Id == id);
if (book != null)
{
Books.Remove(book);
return true;
}
return false;
}
}
Не забудьте зарегистрировать Mutation в Program.cs:
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>();
Теперь в Banana Cake Pop вы можете выполнить мутацию:
mutation {
addBook(title: "Мастер и Маргарита", author: "Михаил Булгаков", year: 1967) {
id
title
author
}
}
Ответ вернёт созданный объект с указанными полями. Обратите внимание: мы сами решаем, какие поля возвращать — это и есть сила GraphQL.
Фильтрация, пагинация и лучшие практики
Фильтрация данных
Hot Chocolate поддерживает фильтрацию «из коробки». Установите пакет:
dotnet add package HotChocolate.Data
Затем добавьте атрибут [UseFiltering] к вашему резолверу:
public class Query
{
[UseFiltering]
public IQueryable<Book> GetBooks() => Books.AsQueryable();
}
Теперь клиент может фильтровать книги:
{
books(where: { author: { eq: "Лев Толстой" } }) {
title
year
}
}
Пагинация
Для пагинации используйте атрибут [UsePaging]:
[UsePaging]
public IQueryable<Book> GetBooks() => Books.AsQueryable();
Запрос с пагинацией будет выглядеть так:
{
books(first: 2) {
nodes {
title
}
pageInfo {