DEV Community

Maria
Maria

Posted on

Building GraphQL APIs with C# and Hot Chocolate

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  
Enter fullscreen mode Exit fullscreen mode

Next, add the HotChocolate.AspNetCore package:

dotnet add package HotChocolate.AspNetCore  
Enter fullscreen mode Exit fullscreen mode

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 }  
    };  
}  
Enter fullscreen mode Exit fullscreen mode

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();  
Enter fullscreen mode Exit fullscreen mode

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  
Enter fullscreen mode Exit fullscreen mode

Then, configure a simple DbContext for books:

public class BookDbContext : DbContext  
{  
    public DbSet<Book> Books { get; set; }  

    public BookDbContext(DbContextOptions<BookDbContext> options) : base(options) { }  
}  
Enter fullscreen mode Exit fullscreen mode

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;  
}  
Enter fullscreen mode Exit fullscreen mode

Finally, register your DbContext and GraphQL schema in Program.cs:

builder.Services.AddDbContext<BookDbContext>(options =>  
    options.UseInMemoryDatabase("Bookstore"));  

builder.Services  
    .AddGraphQLServer()  
    .AddQueryType<Query>();  
Enter fullscreen mode Exit fullscreen mode

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;  
}  
Enter fullscreen mode Exit fullscreen mode

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;  
    }  
}  
Enter fullscreen mode Exit fullscreen mode

Register the Subscription and Mutation

builder.Services  
    .AddGraphQLServer()  
    .AddQueryType<Query>()  
    .AddMutationType<Mutation>()  
    .AddSubscriptionType<Subscription>()  
    .AddInMemorySubscriptions();  

app.MapGraphQL();  
app.UseWebSockets();  
Enter fullscreen mode Exit fullscreen mode

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:

  1. Hot Chocolate Documentation
  2. GraphQL.org
  3. 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)