DEV Community

Cover image for Consuming GraphQL API In ASP.NET Core (Part 2)
John Ojo
John Ojo

Posted on

15 1

Consuming GraphQL API In ASP.NET Core (Part 2)

Here's the second part of the post HotChocolate: Introduction to GraphQL for ASP.NET Core (Part 1). In part 1, we developed a GraphQL API using ASP.NET. In this part, we will focus on consuming the endpoint in an ASP.NET Blazor WebAssembly application.

We will create a page that will display all the Employees along-side their Department, another page that displays a single Employee and a page with a form to create an Employee.

The code for this sample can be found on the CloudBloq/GraphQLSampleAppUI repository on GitHub.

Prerequisites

Create the Solution and Project

After opening VS, click the Create a new project link then search for Blazor and select it:
BlazorSearch

Give the project and solution a name GraphQLSampleAppUI then click create:
NameProAndSolu

Select the Blazor WebAssembly App option then click create:

Template

Install Dependencies

We will make use of GraphQL.Client(v 3.2.0) library to consume the GraphQL API. To install the package, right click the solution in the solution explorer and select Manage NuGet Packages for Solution. Under the Browse section, search for GraphQL.Client and click on it, then in the preview panel click the Install button:

Client

Also, install the modernhttpclient and GraphQL.Client.Serializer.Newtonsoft packages respectively.

Consume The API

In part 1, to test the Query or Mutation, we used Banana Cake Pop:
mutation
query1

In this case, we will make use of the GraphQL.Client library to consume the GraphQL API. We will write all the code required to perform the Query and Mutation in a folder. Add a new folder called DataAccess.

Query

We will create a generic method that can be used to perform all the Query we need to perform. In the DataAccess folder, add a new C# class file called Query.cs:

using GraphQL;
using GraphQL.Client.Http;
using ModernHttpClient;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using GraphQL.Client.Serializer.Newtonsoft;
using Newtonsoft.Json;
namespace GraphQLSampleAppUI.DataAccess
{
public class Query
{
private static GraphQLHttpClient graphQLHttpClient;
static Query()
{
var uri = new Uri("https://localhost:44351/graphql");
var graphQLOptions = new GraphQLHttpClientOptions
{
EndPoint = uri,
HttpMessageHandler = new NativeMessageHandler(),
};
graphQLHttpClient = new GraphQLHttpClient(graphQLOptions, new NewtonsoftJsonSerializer());
}
public static async Task<List<T>> ExceuteQueryReturnListAsyn<T>(string graphQLQueryType, string completeQueryString)
{
try
{
var request = new GraphQLRequest
{
Query = completeQueryString
};
var response = await graphQLHttpClient.SendQueryAsync<object>(request);
var stringResult = response.Data.ToString();
stringResult = stringResult.Replace($"\"{graphQLQueryType}\":", string.Empty);
stringResult = stringResult.Remove(0, 1);
stringResult = stringResult.Remove(stringResult.Length - 1, 1);
var result = JsonConvert.DeserializeObject<List<T>>(stringResult);
return result;
}
catch (Exception ex)
{
throw;
}
}
public static async Task<T> ExceuteQueryAsyn<T>(string graphQLQueryType, string completeQueryString)
{
try
{
var request = new GraphQLRequest
{
Query = completeQueryString
};
var response = await graphQLHttpClient.SendQueryAsync<object>(request);
var stringResult = response.Data.ToString();
stringResult = stringResult.Replace($"\"{graphQLQueryType}\":", string.Empty);
stringResult = stringResult.Remove(0, 1);
stringResult = stringResult.Remove(stringResult.Length - 1, 1);
var result = JsonConvert.DeserializeObject<T>(stringResult);
return result;
}
catch (Exception ex)
{
throw;
}
}
}
}
view raw Query.cs hosted with ❤ by GitHub

The way GraphQL.Client works is to first create an instance of GraphQLHttpClient using an instance of GraphQLHttpClientOptions (which takes in the URI of the GraphQL Server) then use an instance of the GraphQLRequest to create the Query or the Mutation string that will be sent to the Server.

Line 15 defines the field graphQLHttpClient of type GraphQLHttpClient. Line 26 assigns a new instance (using an instance of GraphQLHttpClientOptions) to the graphQLHttpClient field. The other parameter is used to specify the deserializer used to deserialize the output.

Note that if you modified line 8 in launchSettings.json in part 1, you also have to modify line 19 here.

Lines 30 to 54 define the generic method ExceuteQueryReturnListAsyn. The generic type T is the type the output will be deserialized into. The parameter graphQLQueryType is the Query type and the parameter completeQueryString will contain the complete Query. From the example below, graphQLQueryType will be allDepartmentsWithEmployee and completeQueryString will be the entire string.



query{
  allDepartmentsWithEmployee{
    name
    employees{
      email
      name
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

In line 34, an instance of GraphQLRequest is created which takes in the complete query string. The Query is sent to the server in line 39. The result gotten from the server will contain the Query type in the beginning so we replace it with an empty string in line 42. The output is deserialized in line 46 to the generic type.

The ExceuteQueryReturnListAsyn is used when the output we are expecting is an array of the generic type. The other method ExceuteQueryAsyn in line 56 is the same as ExceuteQueryReturnListAsyn except that it should be used when we are expecting a single object of the generic type as the output. The ExceuteQueryReturnListAsyn and ExceuteQueryAsyn methods can be used to perform any Query we need and deserialize the output accordingly.

Mutation

In the DataAccess folder, add a new C# class file called Mutation.cs:

using GraphQL;
using GraphQL.Client.Http;
using GraphQL.Client.Serializer.Newtonsoft;
using ModernHttpClient;
using Newtonsoft.Json;
using System;
using System.Threading.Tasks;
namespace GraphQLSampleAppUI.DataAccess
{
public class Mutation
{
private static GraphQLHttpClient graphQLHttpClient;
static Mutation()
{
var uri = new Uri("https://localhost:44351/graphql");
var graphQLOptions = new GraphQLHttpClientOptions
{
EndPoint = uri,
HttpMessageHandler = new NativeMessageHandler(),
};
graphQLHttpClient = new GraphQLHttpClient(graphQLOptions, new NewtonsoftJsonSerializer());
}
public static async Task<T> ExceuteMutationAsyn<T>(string graphQLQueryType, string completeQueryString)
{
try
{
var request = new GraphQLRequest
{
Query = completeQueryString
};
var response = await graphQLHttpClient.SendMutationAsync<object>(request);
var stringResult = response.Data.ToString();
stringResult = stringResult.Replace($"\"{graphQLQueryType}\":", string.Empty);
stringResult = stringResult.Remove(0, 1);
stringResult = stringResult.Remove(stringResult.Length - 1, 1);
var result = JsonConvert.DeserializeObject<T>(stringResult);
return result;
}
catch (Exception ex)
{
throw;
}
}
}
}
view raw Mutation.cs hosted with ❤ by GitHub

This code in this class follows the same pattern as the Query.cs. Line 27 defines the generic method ExceuteMutationAsyn, it takes in the same parameter as the ExceuteQueryAsyn, sends the Mutation to the server and deserializes the output to the generic type T.

The Models

We will need the models that the Query and Mutation output will be deserialized into. Add a new folder Model in the DataAccess folder. Add a new C# class Employee.cs to the Model folder:

namespace GraphQLSampleAppUI.DataAccess.Model
{
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
public override string ToString()
{
return $"EmployeeId: {EmployeeId},\n" +
$"Name: {Name},\n" +
$"Email: {Email},\n" +
$"Age: {Age},\n" +
$"DepartmentId: {DepartmentId},\n" +
$"Department:[ {Department.ToString()}]\n";
}
}
}
view raw Employee.cs hosted with ❤ by GitHub

Add another C# class file Department.cs to the Model folder:

using System.Collections.Generic;
namespace GraphQLSampleAppUI.DataAccess.Model
{
public class Department
{
public int DepartmentId { get; set; }
public string Name { get; set; }
public ICollection<Employee> Employees { get; set; }
public override string ToString()
{
return $"DepartmentId: {DepartmentId},\n" +
$"Name: {Name}\n";
}
}
}
view raw Department.cs hosted with ❤ by GitHub

We need another model that will be used for the form used to create an Employee. Add another C# class file CreateEmployeeModel.cs to the Model folder:

using System;
using System.ComponentModel.DataAnnotations;
namespace GraphQLSampleAppUI.DataAccess.Model
{
public class CreateEmployeeModel
{
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
[Required(ErrorMessage = "Email is required")]
[EmailAddress]
public string Email { get; set; }
[Required(ErrorMessage = "Department Name is required")]
public string DepartmentName { get; set; }
[Required(ErrorMessage = "Age Name is required")]
[Range(minimum: 20, maximum: 50)]
public int Age { get; set; }
}
}

The validations added to the properties (Required, EmailAddress...) via attributes will be used by the form to validate the input.

We need another model that will be used to hold the output of response when we create an Employee. Add another C# class file CreateEmployeeReturnModel.cs to the Model folder:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace GraphQLSampleAppUI.DataAccess.Model
{
public class CreateEmployeeReturnModel
{
public int DepartmentId { get; set; }
public string Name { get; set; }
public int EmployeeId { get; set; }
}
}

Blazor Pages

First, we will create the page that will be used to display all the Employees. In the Pages folder, add a new Razor Component called EmployeeView.razor:

@page "/EmployeeView"
@using GraphQLSampleAppUI.DataAccess;
@using GraphQLSampleAppUI.DataAccess.Model;
@inject NavigationManager NavigationManager
<h1>All Employee</h1>
<a href="/CreateEmployee">Create an Employee</a>
@if (AllEmployees.Count == 0)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Age</th>
<th>Department Name</th>
</tr>
</thead>
<tbody>
@foreach (var emp in AllEmployees)
{
<tr>
<td><a href="/ViewEmployee/@emp.EmployeeId">@emp.Name</a> </td>
<td>@emp.Email</td>
<td>@emp.Age</td>
<td>@emp.Department.Name</td>
</tr>
}
</tbody>
</table>
}
@code {
private List<Employee> AllEmployees = new List<Employee>();
protected override async Task OnInitializedAsync()
{
try
{
string completeQuery = "query{allEmployeeWithDepartment{name,age,departmentId,email,employeeId,department{name,departmentId}}}";
string graphQLQueryType = "allEmployeeWithDepartment";
var result = await Query.ExceuteQueryReturnListAsyn<Employee>(graphQLQueryType, completeQuery);
AllEmployees = result;
}
catch (Exception ex)
{
throw ex;
}
}
}

The variable AllEmployees on line 43 is used to store the Employees. You see how I called the ExceuteQueryReturnListAsyn passing in the type Employee and the parameters. The variable AllEmployees is checked first if it contains any item on line 13, if it does then the data is displayed in the table in lines 19 to 39.

Next, we will create the page that will be used to display the individual Employee detail. In the Pages folder, add a new Razor Component called ViewEmployee.razor:

@page "/ViewEmployee/{Id:int}"
@using GraphQLSampleAppUI.DataAccess;
@using GraphQLSampleAppUI.DataAccess.Model;
@inject NavigationManager NavigationManager
@if (employee.Name == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>@employee.Name Details</h2>
<div >
<div>
Age:<span style="font-size:large">@employee.Age</span>
<br />
</div>
<br />
<div>
Email: <span style="font-size:large">@employee.Email</span>
<br />
</div>
<br />
<div>
Department Name: <span style="font-size:large">@employee.Department.Name</span>
<br />
</div>
</div>
}
<br />
<br />
<a href="/EmployeeView">Back to List</a>
@code {
[Parameter]
public int? Id { get; set; }
private Employee employee = new Employee();
protected override async Task OnInitializedAsync()
{
string completeQuery = $"query{{employeeById(id:{Id}){{age,department{{departmentId,name}}departmentId,email,employeeId,name}}}}";
string graphQLQueryType = "employeeById";
var result = await Query.ExceuteQueryAsyn<Employee>(graphQLQueryType, completeQuery);
if(result == null)
{
NavigationManager.NavigateTo($"EmployeeView");
}
employee = result;
}
}

The ExceuteQueryAsyn method is used to get the Employee details in line 54. The Id is gotten from the URI and is attached to the string sent to the server in line 51.

Next, we will create the page that will be used to create an Employee. In the Pages folder, add a new Razor Component called CreateEmployee.razor:

@page "/CreateEmployee"
@using GraphQLSampleAppUI.DataAccess;
@using GraphQLSampleAppUI.DataAccess.Model;
@inject NavigationManager NavigationManager
<h1>Create An Employee</h1>
<div class="row">
<EditForm Model="@createEmployee" OnValidSubmit="@CreateButtonClick">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group">
Name: <InputText id="name" @bind-Value="createEmployee.Name" class="form-control" />
</div>
<div class="form-group">
Email: <InputText id="email" @bind-Value="createEmployee.Email" class="form-control" />
</div>
<div class="form-group">
Age: <InputNumber class="form-control" @bind-Value="createEmployee.Age" ParsingErrorMessage="Must be an integer value between 20 and 50" />
</div>
<div class="form-group">
Department Name: <InputText class="form-control" id="departmentName" @bind-Value="createEmployee.DepartmentName" />
</div>
<button type="submit" class="btn btn-success">Create</button>
</EditForm>
</div>
<br />
<br />
<a href="/EmployeeView">Back to List</a>
@code {
private CreateEmployeeReturnModel createEmployeeReturnModel = new CreateEmployeeReturnModel();
private CreateEmployeeModel createEmployee = new CreateEmployeeModel();
protected override async Task OnInitializedAsync()
{
}
private async Task CreateButtonClick()
{
try
{
string completeQuery = $"mutation{{createEmployeeWithDepartment(name:\"{createEmployee.Name}\",age:{createEmployee.Age},email:\"{createEmployee.Email}\",departmentName:\"{createEmployee.DepartmentName}\"){{name,employeeId,departmentId}}}}";
string graphQLQueryType = "createEmployeeWithDepartment";
var result = await Mutation.ExceuteMutationAsyn<CreateEmployeeReturnModel>(graphQLQueryType, completeQuery);
createEmployeeReturnModel = result;
NavigationManager.NavigateTo($"ViewEmployee/{createEmployeeReturnModel.EmployeeId}");
}
catch (Exception ex)
{
throw;
}
}
}

Lines 12 to 33 define the form used to receive the data. The form uses the model CreateEmployeeModel via the variable createEmployee. The method ExceuteMutationAsyn is called on line 56, and the type CreateEmployeeReturnModel is passed as the return type. Notice how the form data is bound to the string on line 53.

Finally, we need to modify the NavMenu.razor in the Shared folder:

<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">GraphQLSampleAppUI</a>
<button class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="EmployeeView">
<span class="oi oi-list-rich" aria-hidden="true"></span> Employee
</NavLink>
</li>
</ul>
</div>
@code {
private bool collapseNavMenu = true;
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
view raw NavMenu.razor hosted with ❤ by GitHub

Running the Application

Ensure the GraphQL server https://localhost:44351/graphql/ is running first. Right-click the solution and click Build Solution. After Build is successful, click CTRL + f5 or f5 to run the application. You should get:

run1

If you click the Employee link, you should get:

run2

Click the Create an Employee link and try to click the Create button without entering any data in the form, you should get the response:

run3

If you enter an invalid email or an age that isn't between 20 and 50, you should get the validation failed error.

This isn't the end of learning Blazor and GraphQL. There are a lot of things to explore, especially in Blazor WebAssembly. The links below will help you if you are really interested in learning more:

Awesome! Please share it with anyone you think could use this information. Thanks for reading. As always, if you have any questions, comments, or concerns about this post, feel free to leave a comment below.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more