GraphQL API с C#: пошаговый туториал на практике
В современной веб-разработке GraphQL становится всё более популярной альтернативой REST API. В отличие от REST, где каждый эндпоинт возвращает фиксированную структуру данных, GraphQL позволяет клиенту запрашивать именно те данные, которые ему нужны. В этом туториале мы создадим полноценный GraphQL API на C# (.NET) с использованием библиотеки Hot Chocolate. Вы узнаете, как настроить сервер, определить типы данных, реализовать запросы и мутации, а также добавить подписки для real-time обновлений.
Что такое GraphQL и почему стоит использовать его с C#?
GraphQL — это язык запросов для API, разработанный Facebook. Основные преимущества:
- Гибкость запросов — клиент сам решает, какие поля получить
- Единая точка входа — один эндпоинт для всех запросов
- Сильная типизация — все типы данных строго определены
- Отсутствие over-fetching и under-fetching — вы получаете ровно то, что запросили
В экосистеме .NET лучшей библиотекой для реализации GraphQL является Hot Chocolate. Она поддерживает все возможности спецификации GraphQL, включая запросы (queries), мутации (mutations) и подписки (subscriptions).
Подготовка проекта и установка зависимостей
Начнем с создания нового проекта ASP.NET Core. Откройте терминал или командную строку и выполните:
dotnet new webapi -n GraphQLDemo
cd GraphQLDemo
Теперь установим пакет Hot Chocolate:
dotnet add package HotChocolate.AspNetCore
dotnet add package HotChocolate.AspNetCore.Playground
Создадим модель данных для нашего API. Допустим, мы будем работать с книгами и авторами. Создайте папку Models и добавьте в неё классы:
// Models/Author.cs
public class Author
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Book> Books { get; set; }
}
// Models/Book.cs
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
Создание GraphQL схемы и резолверов
В GraphQL ключевыми компонентами являются типы, запросы (Query) и мутации (Mutation). Создадим папку GraphQL и добавим в неё файлы:
// GraphQL/Query.cs
public class Query
{
public IQueryable<Book> GetBooks([Service] ApplicationDbContext context)
{
return context.Books.Include(b => b.Author);
}
public Book GetBook(int id, [Service] ApplicationDbContext context)
{
return context.Books.Include(b => b.Author).FirstOrDefault(b => b.Id == id);
}
public IQueryable<Author> GetAuthors([Service] ApplicationDbContext context)
{
return context.Authors.Include(a => a.Books);
}
}
// GraphQL/Mutation.cs
public class Mutation
{
public async Task<Book> AddBook(string title, int authorId, [Service] ApplicationDbContext context)
{
var book = new Book { Title = title, AuthorId = authorId };
context.Books.Add(book);
await context.SaveChangesAsync();
return book;
}
public async Task<Author> AddAuthor(string name, [Service] ApplicationDbContext context)
{
var author = new Author { Name = name };
context.Authors.Add(author);
await context.SaveChangesAsync();
return author;
}
}
Теперь зарегистрируем эти классы в Program.cs:
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddDbContext<ApplicationDbContext>(options =>
options.UseInMemoryDatabase("GraphQLDemo"))
.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddFiltering()
.AddSorting();
var app = builder.Build();
app.MapGraphQL();
app.UsePlayground();
app.Run();
Обратите внимание на методы AddFiltering() и AddSorting() — они добавляют возможности фильтрации и сортировки прямо в GraphQL запросы.
Настройка базы данных и начальных данных
Добавим контекст базы данных и сидер для тестовых данных:
// Data/ApplicationDbContext.cs
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
public DbSet<Book> Books { get; set; }
public DbSet<Author> Authors { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Book>()
.HasOne(b => b.Author)
.WithMany(a => a.Books)
.HasForeignKey(b => b.AuthorId);
}
}
// Data/DataSeeder.cs
public static class DataSeeder
{
public static void Seed(ApplicationDbContext context)
{
if (context.Authors.Any()) return;
var authors = new List<Author>
{
new Author { Id = 1, Name = "Лев Толстой" },
new Author { Id = 2, Name = "Фёдор Достоевский" }
};
var books = new List<Book>
{
new Book { Id = 1, Title = "Война и мир", AuthorId = 1 },
new Book { Id = 2, Title = "Анна Каренина", AuthorId = 1 },
new Book { Id = 3, Title = "Преступление и наказание", AuthorId = 2 }
};
context.Authors.AddRange(authors);
context.Books.AddRange(books);
context.SaveChanges();
}
}
Добавим вызов сидера в Program.cs:
// Program.cs (дополнение)
using var scope = app.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
DataSeeder.Seed(context);
Запросы, мутации и подписки на практике
Запустите приложение командой dotnet run и откройте Playground (обычно по адресу http://localhost:5000/graphql). Вот примеры запросов:
Базовый запрос (Query):
query {
books {
title
author {
name
}
}
}
Запрос с фильтрацией:
query {
books(where: { title: { contains: "Война" } }) {
id
title
author {
name
}