1. Introduction
Modern applications—especially SPAs and mobile apps—require APIs that are flexible, efficient, and scalable. Traditional REST APIs often return fixed data structures, leading to problems like over-fetching (getting more data than needed) or under-fetching (requiring multiple API calls).
GraphQL solves these problems by allowing clients to request exactly the data they need through a single endpoint, guided by a strongly typed schema.
In this article, we will build a Todo API using GraphQL in .NET, step by step, using Hot Chocolate, one of the most mature GraphQL libraries for .NET.
This guide is intentionally detailed and suitable for:
- Backend .NET developers
- Developers migrating from REST to GraphQL
- Teams building APIs for Angular, React, or mobile apps
2. What is GraphQL?
GraphQL is a query language for APIs and a runtime for executing those queries using your existing data.
Key Characteristics
- Single Endpoint: Typically
/graphql - Strongly Typed Schema: Contract between client and server
- Client-driven Queries: Client decides the shape of the response
- Introspection: APIs are self-documenting
REST vs GraphQL (Quick Comparison)
| Feature | REST | GraphQL |
|---|---|---|
| Endpoints | Multiple | Single |
| Over-fetching | Common | Eliminated |
| Under-fetching | Common | Eliminated |
| Versioning | Required | Usually not needed |
| Schema | Implicit | Explicit & Typed |
3. Why Use GraphQL for a Todo API?
Although a Todo API is simple, it demonstrates real-world GraphQL benefits:
- Query only
idandtitlewhen needed - Fetch a single Todo or all Todos with the same endpoint
- Combine multiple operations in one request
- Clean evolution of API without breaking clients
4. Project Setup
4.1 Create the Project
dotnet new webapi -n TodoGraphQLApi
cd TodoGraphQLApi
Remove sample files like WeatherForecast.cs to keep the solution clean.
4.2 Install Required NuGet Packages
We will use Hot Chocolate for GraphQL support:
dotnet add package HotChocolate.AspNetCore
dotnet add package HotChocolate.Data
dotnet add package HotChocolate.Types
Why Hot Chocolate?
- Native .NET experience
- Schema-first and code-first support
- Excellent performance
- Actively maintained
5. Designing the Domain Model
5.1 Todo Entity
public class TodoItem
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public bool IsCompleted { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
This model will later map directly to GraphQL types.
6. Data Access Layer (Repository Pattern)
To keep GraphQL resolvers clean and testable, we use a repository layer.
6.1 Repository Interface
public interface ITodoRepository
{
IEnumerable<TodoItem> GetAll();
TodoItem? GetById(int id);
TodoItem Add(TodoItem todo);
TodoItem? Update(int id, bool isCompleted);
bool Delete(int id);
}
6.2 In-Memory Repository Implementation
public class TodoRepository : ITodoRepository
{
private readonly List<TodoItem> _todos = new();
private int _idCounter = 1;
public IEnumerable<TodoItem> GetAll() => _todos;
public TodoItem? GetById(int id)
=> _todos.FirstOrDefault(t => t.Id == id);
public TodoItem Add(TodoItem todo)
{
todo.Id = _idCounter++;
_todos.Add(todo);
return todo;
}
public TodoItem? Update(int id, bool isCompleted)
{
var todo = GetById(id);
if (todo == null) return null;
todo.IsCompleted = isCompleted;
return todo;
}
public bool Delete(int id)
{
var todo = GetById(id);
if (todo == null) return false;
_todos.Remove(todo);
return true;
}
}
In real projects, this can be replaced with Entity Framework + SQL Server/PostgreSQL without changing GraphQL logic.
7. GraphQL Schema Concepts
GraphQL APIs are built around:
- Query – Read operations
- Mutation – Write operations
- Type System – Object, Input, Enum, Scalar
Hot Chocolate automatically infers schema from C# classes.
8. Implementing GraphQL Queries
Queries fetch data and must be side-effect free.
public class Query
{
public IEnumerable<TodoItem> GetTodos([Service] ITodoRepository repo)
=> repo.GetAll();
public TodoItem? GetTodoById(int id, [Service] ITodoRepository repo)
=> repo.GetById(id);
}
Example Query
query {
todos {
id
title
isCompleted
}
}
9. Implementing GraphQL Mutations
Mutations modify server-side data.
public class Mutation
{
public TodoItem AddTodo(string title, [Service] ITodoRepository repo)
{
return repo.Add(new TodoItem
{
Title = title,
IsCompleted = false
});
}
public TodoItem? CompleteTodo(int id, [Service] ITodoRepository repo)
{
return repo.Update(id, true);
}
public bool DeleteTodo(int id, [Service] ITodoRepository repo)
{
return repo.Delete(id);
}
}
Example Mutation
mutation {
addTodo(title: "Build GraphQL API") {
id
title
isCompleted
}
}
10. Dependency Injection & Configuration
10.1 Register Services
builder.Services.AddSingleton<ITodoRepository, TodoRepository>();
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>();
10.2 Enable GraphQL Endpoint
app.MapGraphQL("/graphql");
Once the app is running, Hot Chocolate provides a built-in GraphQL IDE.
11. Testing the API
Navigate to:
https://localhost:{port}/graphql
You can:
- Explore schema via documentation panel
- Run queries and mutations
- Validate requests automatically
12. Error Handling in GraphQL
GraphQL always returns 200 OK, but errors are included in the response:
{
"data": null,
"errors": [
{
"message": "Todo not found"
}
]
}
This makes client-side handling predictable.
13. Security Considerations
In real-world applications:
- Use Authorization directives
- Limit query depth & complexity
- Validate input using input types
- Enable persisted queries
Hot Chocolate supports all of these features.
14. When to Choose GraphQL
GraphQL is ideal when:
- Multiple clients consume the same API
- Frontend requirements change frequently
- You want strong contracts without versioning
Avoid GraphQL when:
- APIs are extremely simple
- Heavy HTTP caching is mandatory
15. Conclusion
Implementing GraphQL in .NET using Hot Chocolate provides a clean, scalable, and modern API architecture. Even a simple Todo API demonstrates how GraphQL replaces multiple REST endpoints with a single, powerful schema.
From here, you can extend this API by adding:
- Entity Framework Core
- Pagination & filtering
- Authentication & authorization
- GraphQL subscriptions for real-time updates
- GraphQL is a future-proof choice for modern .NET applications.
Happy Coding!