06GraphQL
GraphQL .NET Practice
An end-to-end GraphQL learning project with a Hot Chocolate API and a Blazor WebAssembly client generated with Strawberry Shake. Its sample blog schema demonstrates Relay pagination, filtering, sorting, DataLoaders, typed mutation errors, global IDs, and WebSocket subscriptions.
Context
The problem
I built this project to study GraphQL end to end—from schema design and database-backed resolvers to a typed client—using a small blog domain that makes nested queries, pagination, and real-time events easy to explore.
Solution
How TeachMate works
A code-first Hot Chocolate API serves authors, posts, comments, and tags from PostgreSQL. A Blazor WebAssembly client uses Strawberry Shake-generated operations to query and mutate the data, while a guided study series and Postman collection make the GraphQL concepts reproducible.
Capabilities
What it includes
- Relay connection pagination with filtering, sorting, projections, and bounded page sizes
- Global object identification with the Node interface and opaque GraphQL IDs
- Nested author and post resolvers with DataLoaders to batch related database lookups
- Post and comment mutations with typed domain errors
- WebSocket subscriptions for post publication and new comments
- Blazor client for browsing, filtering, paging, creating posts, and receiving subscription events
- Guided study series, schema tour, query cookbook, and runnable Postman operations
System design
Architecture
- A .NET 10 ASP.NET Core API uses Hot Chocolate 16 in a modular-monolith structure, with separate Authors, Posts, Comments, and Tags modules.
- Entity Framework Core 10 and Npgsql persist the blog domain in PostgreSQL; migrations and development seed data support local setup.
- The Blazor WebAssembly client uses Strawberry Shake 16 to generate a typed C# client from GraphQL operation documents.
- The API exposes GraphQL over HTTP and WebSocket subscriptions, plus Nitro for exploration and an SDL schema endpoint.
Trade-offs
Engineering decisions
- Organize schema types, resolvers, data configuration, and registrations by domain module while sharing the blog entities and database context.
- Use Relay connections and global node IDs to demonstrate stable cursor-based pagination and client-independent object identity.
- Apply selection-aware EF queries and DataLoaders to avoid unnecessary data retrieval and repeated nested lookups.
- Use in-memory subscriptions for local learning and explicitly keep authentication, authorization, and distributed subscription infrastructure out of scope.