Building GraphQL APIs with C# and Hot Chocolate
Introduction: Why Should You Care About GraphQL?
In today's world of modern applications, data is king. Whether you're building a web app, mobile app, or IoT solution, your users expect fast, reliable, and flexible ways to interact with data. But when it comes to APIs, traditional REST architectures often fall short: they can be over-fetching, under-fetching, or downright inflexible for complex client needs.
Enter GraphQL, a query language and runtime for APIs that allows clients to request exactly the data they need—no more, no less. With GraphQL, you can design APIs that are self-documenting, strongly typed, and highly customizable, empowering developers to build faster, more scalable apps.
In this blog post, we'll explore how to build modern GraphQL APIs using C# and the Hot Chocolate library. We'll cover schema design, resolvers, subscriptions for real-time data, and best practices, all while keeping the process approachable yet detailed for experienced C# developers.
Let’s dive in!
What is Hot Chocolate?
Hot Chocolate is a powerful and flexible GraphQL server library for .NET. It allows you to build GraphQL APIs quickly and efficiently using the modern .NET stack. Some of its standout features include:
- Code-first schema design: Define your GraphQL schema directly using C# classes and attributes.
- Built-in support for dependency injection: Seamlessly integrate with ASP.NET Core.
- Subscriptions: Enable real-time capabilities with ease.
- Performance: Optimized for high performance with minimal overhead.
Now that we know what Hot Chocolate is, let’s get started building a GraphQL API step by step.
Setting Up Your Project
Before we can start writing code, we need to set up our development environment.
Prerequisites
- .NET 6 or higher installed on your machine
- Familiarity with C# and ASP.NET Core
- A working knowledge of GraphQL basics
Step 1: Create a New ASP.NET Core Project
First, create a new ASP.NET Core Web API project:
dotnet new webapi -n HotChocolateGraphQLDemo
cd HotChocolateGraphQLDemo
Next, add the HotChocolate.AspNetCore package:
dotnet add package HotChocolate.AspNetCore
This package includes everything you need to build and host a GraphQL API with Hot Chocolate.
Designing Your GraphQL Schema
Code-First Schema Design
Hot Chocolate uses a code-first approach for defining your GraphQL schema. This means you define your schema using C# classes and attributes, rather than writing a schema definition in SDL (Schema Definition Language).
Here’s an example schema for a simple "Bookstore" API:
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public decimal Price { get; set; }
}
public class Query
{
public List<Book> GetBooks() => new()
{
new Book { Id = 1, Title = "1984", Author = "George Orwell", Price = 9.99m },
new Book { Id = 2, Title = "The Hobbit", Author = "J.R.R. Tolkien", Price = 14.99m }
};
}
Registering the Schema
To make this schema accessible via GraphQL, we need to register it in the Startup.cs file (or Program.cs
if using .NET 6 minimal APIs):
var builder = WebApplication.CreateBuilder(args);
// Add GraphQL services
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>();
var app = builder.Build();
// Map GraphQL endpoint
app.MapGraphQL();
app.Run();
That's it! You now have a fully functional GraphQL API with a query that retrieves books.
Resolving Data Dynamically
Hardcoding data is fine for demos, but real-world APIs fetch data from databases or external APIs. Let’s update the GetBooks
resolver to fetch data dynamically.
Example: Fetching Data from a Database
First, install the Microsoft.EntityFrameworkCore package to use Entity Framework Core:
dotnet add package Microsoft.EntityFrameworkCore
Then, configure a simple DbContext for books:
public class BookDbContext : DbContext
{
public DbSet<Book> Books { get; set; }
public BookDbContext(DbContextOptions<BookDbContext> options) : base(options) { }
}
Update the Query
class to use dependency injection for fetching data:
public class Query
{
private readonly BookDbContext _dbContext;
public Query(BookDbContext dbContext)
{
_dbContext = dbContext;
}
public IQueryable<Book> GetBooks() => _dbContext.Books;
}
Finally, register your DbContext and GraphQL schema in Program.cs
:
builder.Services.AddDbContext<BookDbContext>(options =>
options.UseInMemoryDatabase("Bookstore"));
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>();
Adding Subscriptions for Real-Time Data
One of GraphQL’s superpowers is its built-in support for subscriptions, which enable real-time updates. For example, you can notify clients about new books added to the database.
Define a Subscription
public class Subscription
{
[Subscribe]
public Book OnBookAdded([EventMessage] Book book) => book;
}
Publish Events in a Mutation
Let’s add a mutation to add books and trigger events:
public class Mutation
{
private readonly BookDbContext _dbContext;
private readonly ITopicEventSender _eventSender;
public Mutation(BookDbContext dbContext, ITopicEventSender eventSender)
{
_dbContext = dbContext;
_eventSender = eventSender;
}
public async Task<Book> AddBook(Book book)
{
_dbContext.Books.Add(book);
await _dbContext.SaveChangesAsync();
await _eventSender.SendAsync(nameof(Subscription.OnBookAdded), book);
return book;
}
}
Register the Subscription and Mutation
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddSubscriptionType<Subscription>()
.AddInMemorySubscriptions();
app.MapGraphQL();
app.UseWebSockets();
Clients can now subscribe to OnBookAdded
and receive real-time updates whenever a new book is added.
Common Pitfalls and How to Avoid Them
1. Overfetching Data
GraphQL allows clients to request only the data they need, but if your resolvers fetch too much from the database, you’ll lose performance benefits. Use projection to limit the data retrieved by your queries.
2. Circular Dependencies
Complex schemas can lead to circular dependencies between types. Use DataLoader to batch and optimize related queries.
3. Subscription Overhead
Subscriptions can be resource-intensive, especially with many clients. Use rate limiting and ensure your server can scale horizontally.
Key Takeaways
- GraphQL APIs provide flexibility and efficiency compared to traditional REST APIs.
- Hot Chocolate makes building GraphQL APIs in C# simple and powerful.
- Use code-first schema design to define your API directly in C#.
- Add subscriptions for real-time capabilities, but be mindful of performance.
- Avoid common pitfalls by optimizing data fetching and schema design.
Next Steps
Ready to dive deeper? Here are some resources:
- Hot Chocolate Documentation
- GraphQL.org
- Experiment with advanced features like middleware, custom scalars, and federation.
GraphQL is more than just an API technology—it's a paradigm shift. By combining it with C# and Hot Chocolate, you’re well-equipped to build modern, scalable applications that delight both developers and users.
Let’s start coding! 🚀
Top comments (0)