Previous gRPC APIs in .NET Core Asynchronous-Messaging Next

GraphQL with .NET Core

🌐 GraphQL with .NET Core

GraphQL is a query language for APIs and a server-side runtime for executing those queries. In contrast to traditional REST APIs, which rely on multiple, fixed endpoints, GraphQL typically uses a single endpoint. This allows clients to request exactly the data they need, addressing issues like over-fetching (getting too much data) and under-fetching (making multiple requests for related data).

In .NET Core, several libraries facilitate GraphQL implementation. One of the most popular is Hot Chocolate, a feature-rich and developer-friendly framework that integrates seamlessly with ASP.NET Core.

📦 Prerequisites

  • .NET 6 or later
  • NuGet packages: HotChocolate.AspNetCore, HotChocolate.Data
  • IDE like Visual Studio or VS Code

🧪 Steps to Create a GraphQL API

1. Create a New Project

dotnet new webapi -n GraphQLDemo

2. Install Hot Chocolate

dotnet add package HotChocolate.AspNetCore

3. Define a Model

public class Book {
    public int Id { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
}
    

4. Create a Query Class

public class Query {
    public Book GetBook() => new Book { Id = 1, Title = "GraphQL Guide", Author = "Shivshanker" };
}
    

5. Configure GraphQL in Program.cs

var builder = WebApplication.CreateBuilder(args);
builder.Services
    .AddGraphQLServer()
    .AddQueryType<Query>();

var app = builder.Build();
app.MapGraphQL();
app.Run();
    

6. Run and Test

Navigate to https://localhost:{port}/graphql and use the Banana Cake Pop UI to test queries like:

{
  book {
    id
    title
    author
  }
}
    

🛠️ Example: Implementing GraphQL with Hot Chocolate in .NET Core

1️⃣ Set up the project

First, create a new ASP.NET Core Web API project.

2️⃣ Install NuGet packages

dotnet add package HotChocolate.AspNetCore
dotnet add package HotChocolate.Data.EntityFramework

3️⃣ Define your data models

// Models/Product.cs
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

4️⃣ Implement the data context and service

// Data/ApplicationDbContext.cs
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    { }

    public DbSet<Product> Products { get; set; }
}

// Services/ProductService.cs
public class ProductService
{
    private readonly ApplicationDbContext _context;

    public ProductService(ApplicationDbContext context) => _context = context;

    public IQueryable<Product> GetProducts() => _context.Products;
}

5️⃣ Define the GraphQL types and schema

// GraphQL/Query.cs
using HotChocolate.Data;

public class Query
{
    [UseProjection]
    [UseFiltering]
    [UseSorting]
    public IQueryable<Product> GetProducts([Service] ProductService service)
    {
        return service.GetProducts();
    }
}

6️⃣ Configure the GraphQL server in Program.cs

var builder = WebApplication.CreateBuilder(args);

// Add services for GraphQL
builder.Services.AddPooledDbContextFactory<ApplicationDbContext>(opt =>
    opt.UseInMemoryDatabase("ProductsDb"));
builder.Services.AddScoped<ProductService>();

builder.Services
    .AddGraphQLServer()
    .AddQueryType<Query>()
    .AddProjections()
    .AddFiltering()
    .AddSorting()
    .AddInMemorySubscriptions(); // For real-time functionality

var app = builder.Build();

// Seed data
await using (var scope = app.Services.CreateAsyncScope())
{
    var context = scope.ServiceProvider.GetRequiredService<IDbContextFactory<ApplicationDbContext>>().CreateDbContext();
    context.Products.AddRange(
        new Product { Id = 1, Name = "Laptop", Price = 1200 },
        new Product { Id = 2, Name = "Keyboard", Price = 75 }
    );
    await context.SaveChangesAsync();
}

app.MapGraphQL();
app.Run();

7️⃣ Test with the built-in IDE

Run the application and navigate to the /graphql endpoint. Hot Chocolate provides a browser-based IDE (Banana Cake Pop) where you can explore the schema and execute queries.

query {
  products(where: { name: { contains: "key" } }, first: 10) {
    name
    price
  }
}

✅ Advantages

  • Reduced over- and under-fetching: Clients get precisely the data they need in a single request, which saves bandwidth and reduces latency.
  • Rapid feature evolution: Adding new fields does not create breaking changes for existing clients.
  • Powerful aggregation: Avoids the "N+1" problem common in REST APIs.
  • Strongly typed and self-documenting: The schema provides a single source of truth for all API capabilities.
  • Real-time updates: Subscriptions enable robust real-time functionality over WebSockets.

⚠️ Disadvantages

  • Increased complexity: Setup and server-side logic are more complex than basic REST APIs.
  • Caching challenges: Traditional HTTP caching is difficult; client-side or server-side strategies are required.
  • Security risks: Deeply nested queries can expose the server to DoS attacks; implement complexity analysis and depth limiting.
  • Steeper learning curve: Teams need to learn GraphQL-specific concepts like schemas, resolvers, and mutations.

❌ Limitations

  • Steeper learning curve than REST
  • Complex caching and error handling
  • Overhead for simple CRUD APIs

📌 When to Use

  • Complex applications with diverse clients.
  • High read-to-write ratio applications.
  • Microservices architecture as a "gateway".
  • Rapidly evolving frontend requirements.

❌ When Not to Use

  • Simple, fixed-data applications: REST may be simpler and easier to cache.
  • Small teams or startups: Added complexity may not be worthwhile.
  • High-performance, low-latency internal APIs: gRPC may be more performant.

💡 Best Practices and Tips

  • Design a strong schema with consistent naming and clear descriptions.
  • Use DataLoader pattern to solve the N+1 problem.
  • Implement query complexity analysis and depth limiting.
  • Enforce authentication and authorization in resolvers.
  • Use Entity Framework's IQueryable with projections and filtering.
  • Use fragments to reuse parts of complex or repetitive queries.

⚠️ Precautions

  • Secure introspection: Restrict or disable in production.
  • Watch for performance bottlenecks with complex relationships.
  • Handle errors gracefully: GraphQL returns 200 OK for all responses; parse payload for errors.
  • Decide on client-side caching using libraries like Apollo Client.
Back to Index
Previous gRPC APIs in .NET Core Asynchronous-Messaging Next
*