GraphQL API with C#: Step-by-Step Practical Tutorial
In modern web development, GraphQL is becoming an increasingly popular alternative to REST API. Unlike REST, where each endpoint returns a fixed data structure, GraphQL allows the client to request exactly the data it needs. In this tutorial, we will create a full-fledged GraphQL API in C# (.NET) using the Hot Chocolate library. You will learn how to set up the server, define data types, implement queries and mutations, and add subscriptions for real-time updates.
What is GraphQL and why use it with C#?
GraphQL is a query language for APIs developed by Facebook. The main advantages are:
- Query flexibility — the client decides which fields to retrieve
- Single entry point — one endpoint for all queries
- Strong typing — all data types are strictly defined
- No over-fetching or under-fetching — you get exactly what you requested
In the .NET ecosystem, the best library for implementing GraphQL is Hot Chocolate. It supports all features of the GraphQL specification, including queries, mutations, and subscriptions.
Project setup and dependency installation
Let's start by creating a new ASP.NET Core project. Open a terminal or command line and run:
dotnet new webapi -n GraphQLDemocd GraphQLDemoNow install the Hot Chocolate package:
dotnet add package HotChocolate.AspNetCoredotnet add package HotChocolate.AspNetCore.PlaygroundLet's create a data model for our API. Suppose we will work with books and authors. Create a Models folder and add the following classes to it:
// Models/Author.cspublic class Author{ public int Id { get; set; } public string Name { get; set; } public ICollection<Book> Books { get; set; }}
// Models/Book.cspublic class Book{ public int Id { get; set; } public string Title { get; set; } public int AuthorId { get; set; } public Author Author { get; set; }}Creating the GraphQL schema and resolvers
In GraphQL, the key components are types, queries, and mutations. Create a GraphQL folder and add the following files to it:
// GraphQL/Query.cspublic 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.cspublic 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; }}Now let's register these classes in Program.cs:
// Program.csvar builder = WebApplication.CreateBuilder(args);
builder.Services .AddDbContext<ApplicationDbContext>(options => options.U