Working with GraphQL API in .NET 8 Using HotChocolate

Hello Everyone

Today, we will delve into one of the modern API architectures.

GraphQL

GraphQL, developed by Facebook, is a modern API architecture that allows clients to request precisely the data they need. Unlike traditional REST APIs, GraphQL provides flexibility and efficiency in fetching data by enabling clients to query multiple resources in a single request.

GraphQL is a powerful query language for APIs, enabling clients to request exactly the data they need. With .NET 8 and the HotChocolate library, building a robust GraphQL API is straightforward. In this article, we'll create a GraphQL API to manage a collection of books using a book repository.

Prerequisites

Before diving in, ensure you have,

  1. .NET 8 SDK installed.
  2. Basic knowledge of C# and GraphQL.

Step 1. Setting Up the Project.

  1. Create a new .NET 8 Web API project

    dotnet new web -n GraphQLApi
    cd GraphQLApi
  2. Add the HotChocolate NuGet packages
    dotnet add package HotChocolate.AspNetCore
    dotnet add package HotChocolate.Data
    

Step 2. Defining the Data Model.

Create a Book class to represent the data.

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

Step 3. Implementing the Repository.

The BookRepository handles CRUD operations. Here's the implementation.

namespace GraphQLApi
{
    public class BookRepository
    {
        private readonly List<Book> _books = new()
        {
            new Book { Id = 1, Title = "C# in Depth", Author = "Jon Skeet" },
            new Book { Id = 2, Title = "Clean Code", Author = "Robert C. Martin" }
        };

        public IQueryable<Book> GetBooks() => _books.AsQueryable();

        public Book GetBook(int id) => _books.FirstOrDefault(x => x.Id == id);

        public Book AddBook(Book book)
        {
            book.Id = _books.Max(b => b.Id) + 1;
            _books.Add(book);
            return book;
        }

        public Book UpdateBook(int id, Book book)
        {
            var bookDetail = GetBook(id);
            if (bookDetail != null)
            {
                _books.Remove(bookDetail);
                book.Id = bookDetail.Id;
                _books.Add(book);
                return book;
            }
            return null;
        }

        public bool DeleteBook(int id)
        {
            var bookDetail = GetBook(id);
            if (bookDetail != null)
            {
                _books.Remove(bookDetail);
                return true;
            }
            return false;
        }
    }
}

Step 4. Defining the GraphQL Schema.

Query Type

The Query class defines methods to fetch data.

using GraphQLApi.Models;
using GraphQLApi.Repositories;

namespace GraphQLApi.GraphQL
{
    public class Query
    {
        private readonly BookRepository _repository;

        public Query(BookRepository repository)
        {
            _repository = repository;
        }

        public IQueryable<Book> GetBooks() => _repository.GetBooks();
        public Book GetBook(int id) => _repository.GetBook(id);
    }
}

Mutation Type

The Mutation class defines methods for modifying data.

namespace GraphQLApi
{
    public class Mutation
    {
        private readonly BookRepository _repository;

        public Mutation(BookRepository repository)
        {
            _repository = repository;
        }

        public Book AddBook(string title, string author)
        {
            var book = new Book { Title = title, Author = author };
            return _repository.AddBook(book);
        }

        public Book UpdateBook(int id, string title, string author)
        {
            var book = new Book { Id = id, Title = title, Author = author };
            return _repository.UpdateBook(id, book);
        }

        public bool DeleteBook(int id)
        {
            return _repository.DeleteBook(id);
        }
    }
}

Step 5. Configuring the GraphQL Server.

Update the Program.cs file to configure the GraphQL server.

using GraphQLApi;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// Add services
builder.Services.AddSingleton<BookRepository>();

// Add GraphQL service support
builder.Services
    .AddGraphQLServer()
    .AddQueryType<Query>()
    .AddMutationType<Mutation>()
    .AddFiltering()
    .AddSorting();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

// Configure the HTTP request pipeline
app.MapGraphQL();
app.Run();

Step 6. Running and Testing the API.

Run the application.

dotnet run

Open the GraphQL Playground in your browser at http://localhost:5000/graphql.

Example Queries and Mutations

Query All Books

query {
  books {
    id
    title
    author
  }
}

Query a Single Book

query {
  bookById(id:2) {
    id
    title
    author
  }
}

Add a New Book

mutation {
  addBook( title: "New Book", author: "Author Name" ) {
    id
    title
    author
  }
}

Update an Existing Book

mutation {
  updateBook(id: 2,  title: "Updated Title", author: "Updated Author" ) {
    id
    title
    author
  }
}

Delete a Book

mutation {
  deleteBook(id: 2)
}

Sample ScreenShot

Sample ScreenShot

GraphQL vs REST Comparision
 

Aspect GraphQL REST
Endpoint Single endpoint for all operations. Multiple endpoints for different resources.
Data Fetching Fetches only required fields in one query. Returns fixed data structures, often over-fetching or under-fetching.
Flexibility Highly flexible; client defines the response. The server defines a response structure that is less flexible.
Performance Combines multiple queries into one request. Requires multiple requests for related resources.
Versioning No versioning is needed; it evolves with schema changes. Requires versioning for major updates (e.g., /v1, /v2).
Real-Time Built-in support for subscriptions (real-time). Limited real-time capabilities; needs additional setup.
Use Cases Best for complex, dynamic, or real-time needs. Ideal for simpler, well-defined APIs.


Conclusion

With .NET 8 and HotChocolate, creating a GraphQL API is simple and efficient. This example demonstrates the core concepts of building and managing an API with queries and mutations. You can extend this implementation by adding advanced features like authorization and subscriptions or integrating with a database. For more details, check out this documentation link GraphQL

Happy coding!


Similar Articles