Building a GraphQL API with HotChocolate - Part 4
In this final post, we explore the implementation of a GraphQL server in C# and demonstrate how it can be queried from an HTML file using Javascript.
The code for this section is publicly available for download on GitHub. Access it by visiting the following repository: EOCS.GraphQL.
As previously mentioned, GraphQL is a specification that can be effectively implemented in various languages. Here, we will explore one such implementation written in C#.
What is HotChocolate ?
HotChocolate is an open-source GraphQL server for the Microsoft .NET platform that is compliant with the newest GraphQL October 2021 spec + Drafts, which makes Hot Chocolate compatible to all GraphQL compliant clients.
https://chillicream.com/docs/hotchocolate/v13
We'll set up a basic GraphQL server using HotChocolate in an Azure Function, and then we'll query it with Javascript.
Creating the environment
Create a new solution named EOCS.GraphQL for example and a new Azure Functions project in it.
Create a new class named Customer.cs and add the following code to it.
1public class Customer
2{
3 public string Id { get; set; }
4
5 public string Name { get; set; }
6
7 public int Age { get; set; }
8}
- Create a new interface named ICustomerRepository.cs and add the following code in it.
1public interface ICustomerRepository
2{
3 List<Customer> GetAllCustomers();
4}
- Create a new class named MockCustomerRepository.cs that implements the ICustomerRepository interface.
1public class MockCustomerRepository : ICustomerRepository
2{
3 public List<Customer> GetAllCustomers()
4 {
5 return new List<Customer>()
6 {
7 new Customer(){ Id = "0001", Name = "Bruce Smith", Age = 45 },
8 new Customer(){ Id = "0010", Name = "Melissa Price", Age = 52 }
9 };
10 }
11}
So far, there's nothing particularly groundbreaking: we're about to create a GET endpoint that returns data about customers.
Using HotChocolate
Add the HotChocolate.AzureFunctions NuGet package.
Add a class named Query.cs with the following code.
1public class Query
2{
3 public List<Customer> GetCustomers([Service] ICustomerRepository customerRepository)
4 {
5 return customerRepository.GetAllCustomers();
6 }
7}
Here is where we start setting up the GraphQL server by defining the ability to query customers. HotChocolate will handle all the necessary infrastructure, allowing us to query only the data we need.
All that's left for us to do is to provide the single entry point.
- Create a class named CustomerService.cs (or a more general name) and add the following code to it.
1public class CustomerService
2{
3 private readonly IGraphQLRequestExecutor _executor;
4
5 public CustomerService(IGraphQLRequestExecutor executor)
6 {
7 _executor = executor;
8 }
9
10 [FunctionName(nameof(Run))]
11 public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "graphql/{**slug}")] HttpRequest req)
12 {
13 return await _executor.ExecuteAsync(req);
14 }
15}
We can witness the significant advantages of GraphQL in action: we only need one endpoint to handle all the requests.
- Create a class named StartUp.cs to bootstrap the Azure Function.
1public class StartUp : FunctionsStartup
2{
3 public override void Configure(IFunctionsHostBuilder builder)
4 {
5 ConfigureServices(builder.Services);
6 }
7
8 private static void ConfigureServices(IServiceCollection services)
9 {
10 services.AddSingleton<ICustomerRepository, MockCustomerRepository>();
11
12 services.AddGraphQLFunction().AddQueryType<Query>();
13 }
14}
Here, we specify that we intend to add GraphQL features. It is important to note that we explicitely specifiy that the class responsible for executing queries will be referred to as Query. Once again, this setup is facilitated by HotChocolate.
Consuming the service
Now it's time to consume our service, and for this, we will create a simple HTML file.
Create a new ASP.NET Core project and add a file named index.html in wwwroot.
Add the following code to it.
1<html>
2<head>
3 <title>GraphQL</title>
4</head>
5<body>
6 <pre><code class="language-json" id="code"></code></pre>
7 <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.9/beautify.min.js"></script>
8 <script>
9 (async function () {
10 const data = JSON.stringify({
11 query: `query {
12 customers {
13 id
14 age
15 }
16 }`
17 });
18
19 const response = await fetch(
20 'http://localhost:7020/api/graphql',
21 {
22 method: 'post',
23 body: data,
24 headers: {
25 'Content-Type': 'application/json'
26 },
27 }
28 );
29
30 const json = await response.json();
31 document.getElementById('code').innerHTML = js_beautify(
32 JSON.stringify(json.data)
33 );
34 })();
35 </script>
36</body>
37</html>
HotChocolate will automatically recognize that the customers object in the query should be mapped to the GetCustomers method defined earlier.
Here, we can observe how we structure the data to retrieve what we need: we simply specify that we are interested in the customers' id and age, and then post it to the /graphql endpoint.
- Edit the Program.cs code.
1public class Program
2{
3 public static void Main(string[] args)
4 {
5 var builder = WebApplication.CreateBuilder(args);
6 var app = builder.Build();
7
8 app.UseDefaultFiles();
9
10 app.UseStaticFiles();
11
12 app.Run(async (context) =>
13 {
14 await context.Response.WriteAsync("Request Handled and Response Generated");
15 });
16
17 app.Run();
18 }
19}
Running the program
Here is the result obtained when the applications are executed.
Now, suppose we are also interested in the name. It's simple: the client just needs to modify its request.
1<html>
2<head>
3 <title>GraphQL</title>
4</head>
5<body>
6 <pre><code class="language-json" id="code"></code></pre>
7 <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.9/beautify.min.js"></script>
8 <script>
9 (async function () {
10 const data = JSON.stringify({
11 query: `query {
12 customers {
13 id
14 name
15 age
16 }
17 }`
18 });
19
20 const response = await fetch(
21 'http://localhost:7020/api/graphql',
22 {
23 method: 'post',
24 body: data,
25 headers: {
26 'Content-Type': 'application/json'
27 },
28 }
29 );
30
31 const json = await response.json();
32 document.getElementById('code').innerHTML = js_beautify(
33 JSON.stringify(json.data)
34 );
35 })();
36 </script>
37</body>
38</html>
We can observe that we can now retrieve only the desired data without the necessity to redeploy additional endpoints. We did not alter the Azure Function code to modify the request or add an additional property.
Final thoughts
If you wish to delve deeper into this topic, acquire the following books, which encompass all the concepts emphasized in this series and delve into more advanced ones.
Learning GraphQL: Declarative Data Fetching for Modern Web Apps (Porcello, Banks)
Apps and Services with .NET 8: Build practical projects with Blazor, .NET MAUI, gRPC, GraphQL, and other enterprise technologies (Price)
This post only provides an overview of the philosophy behind GraphQL. We have omitted many complexities associated with this specification, including advanced features such as subscriptions or mutations. These concepts will be addressed in a more advanced tutorial. Refer to the following series for more details.
Do not hesitate to contact me shoud you require further information.