I'm always excited to take on new projects and collaborate with innovative minds.
Learn how to build a modern, scalable GraphQL API with .NET 8 and Hot Chocolate. This POC demonstrates querying, mutations, filtering, sorting, and cursor-based pagination with EF Core InMemory β a clean foundation for production-ready APIs.
This proof of concept (POC) demonstrates how to build a modern GraphQL API on .NET 8, powered by Hot Chocolate and Entity Framework Core.
It covers querying, mutations, filtering, sorting, projections, and cursor-based pagination β all backed by an EF Core InMemory provider for fast iteration and testing.
GraphQL is more than a trend β itβs a paradigm shift in how clients interact with APIs.
| Layer | Technology | Purpose |
|---|---|---|
| Backend | ASP.NET Core (net8.0) | Hosting, middleware, dependency injection |
| GraphQL Engine | Hot Chocolate | Schema, resolvers, projections, filtering, sorting |
| Database | EF Core InMemory | Lightweight data storage for development |
| REST Support | Swashbuckle (Swagger) | Legacy REST endpoint for comparison |
| File | Purpose |
|---|---|
GraphQL\Query.cs | Exposes GraphQL query resolvers |
GraphQL\Mutation.cs | Defines mutations (writes) |
Data\AppDbContext.cs | EF Core configuration |
Models\Author.cs, Models\Book.cs | Domain entities |
Program.cs | Application entry point β wires GraphQL, EF Core, and seeds data |
Key Paths
d:\Projects\GraphQLApi.POC\GraphQLApi.POC\
β
βββ Program.cs
βββ GraphQL\
β βββ Query.cs
β βββ Mutation.cs
βββ Data\
βββ AppDbContext.cs
public class Author {
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public ICollection<Book> Books { get; set; } = new List<Book>();
}
public class Book {
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public int PublishedYear { get; set; }
public int AuthorId { get; set; }
public Author? Author { get; set; }
}
Author has multiple Books.Hot Chocolate allows seamless integration between EF Core and GraphQL by returning IQueryable<T> β enabling powerful server-side filtering, sorting, and projections.
public class Query
{
[UsePaging(MaxPageSize = 50, IncludeTotalCount = true)]
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Book> GetBooks([Service] AppDbContext db) => db.Books;
[UsePaging(MaxPageSize = 50, IncludeTotalCount = true)]
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Author> GetAuthors([Service] AppDbContext db) => db.Authors;
}
A simple mutation example to create a new book:
public class Mutation
{
public async Task<Book> AddBook(CreateBookInput input, [Service] AppDbContext db)
{
var book = new Book {
Title = input.Title,
PublishedYear = input.PublishedYear,
AuthorId = input.AuthorId
};
db.Books.Add(book);
await db.SaveChangesAsync();
return book;
}
}
public record CreateBookInput(string Title, int PublishedYear, int AuthorId);
UsePaging provides cursor-based pagination (not offset-based), delivering both totalCount and navigational cursors.
query {
books(first: 2) {
totalCount
pageInfo { hasNextPage endCursor }
edges {
cursor
node {
id
title
publishedYear
author { id name }
}
}
}
}
Example pagination query with a cursor:
query($cursor: String!) {
books(first: 2, after: $cursor) {
pageInfo { hasNextPage endCursor }
edges { node { id title } }
}
}
dotnet build
dotnet run
Endpoints
Seeded Data
| Author | Books |
|---|---|
| Isaac Asimov | Foundation (1951) |
| Ursula K. Le Guin | The Left Hand of Darkness (1969) |
query {
books(order: { publishedYear: DESC }) {
edges {
node {
id
title
publishedYear
author { id name }
}
}
totalCount
pageInfo { hasNextPage endCursor }
}
}
query {
authors(where: { name: { contains: "Ursula" } }) {
edges { node { id name books { title } } }
totalCount
}
}
mutation {
addBook(input: { title: "The Dispossessed", publishedYear: 1974, authorId: 2 }) {
id
title
author { id name }
}
}
MaxPageSize.| Area | Enhancement | Description |
|---|---|---|
| Database | Switch to SQLite or SQL Server | Replace UseInMemoryDatabase with UseSqlite or UseSqlServer |
| Auth | Add authentication & authorization | Use AddAuthorization(), decorate resolvers with [Authorize] |
| Observability | Add logging & tracing | Integrate with OpenTelemetry for distributed tracing |
| Validation | Input validation | Integrate FluentValidation or HotChocolate input validators |
| Error Handling | Custom error filters | Consistent error payloads for API consumers |
| Testing | Integration & resolver tests | Use HttpClient and test GraphQL endpoints directly |
To go beyond this POC:
This POC shows how easy it is to build a modern, maintainable GraphQL API using .NET 8 and Hot Chocolate.
With minimal configuration, you get a type-safe, self-documenting, high-performance API that scales gracefully.
The built-in GraphQL IDE makes exploration effortless β run queries, test mutations, and extend your schema with confidence.
β‘ With just a few enhancements, this POC can evolve into a fully production-grade service.
Your email address will not be published. Required fields are marked *