Showing posts with label ASP.NET Core. Show all posts
Showing posts with label ASP.NET Core. Show all posts

Wednesday, 3 May 2023

GraphQL API with ASP.NET Core— Making your API smart

 GraphQL was created in 2012 and open-sourced by Facebook in 2015. In 2019, Facebook and others created the GraphQL Foundation as a neutral, non-profit home for the GraphQL assets and ongoing collaboration, and hosted by The Linux Foundation. The GraphQL Foundation is a fully neutral home for the GraphQL trademark and provides a means of collecting and distributing membership dues to support core community infrastructure and programs.

What is GraphQL

GraphQL is a query language for APIs, it gives power to the client to control the response on what they need instead of the server deciding what to respond.

It sits between clients and backend services and fulfills the query for clients. GraphQL can aggregate multiple resource requests into a single query.

REST API VS GraphQL API

Let's see some commonalities and differences between REST and GraphQL APIs which will help to take a call on their uses.

In REST:

  • The query endpoint is simple i.e. https://localhost:7025/employees?id=2
  • The Server decides what data to send back as the response.
  • Multiple API endpoints are required to pull multiple resources i.e. can’t pull employee as well as department data in a single API call.
  • Easy to cache the response because of REST’s HTTP GET has a well-defined caching behavior leveraged by browsers, CDNs, proxies, web servers, etc.
  • Doesn't require any special library to consume REST APIs

In GraphQL:

How to implement

Let's see how to implement GraphQL API in ASP.NET Core through an example.

Problem Statement: Need to develop APIs to query Employees and their departments. For demo purposes, I’m keeping the API simple and hardcoding the data hence there won’t be any db connections.

First thing, we would create an ASP.NET Core web API project and follow the steps below.

Step 1: Install the required Nuget packages.

GraphQL.Server.All

This Nuget package has everything you need.

Step 2: Define the model. Create a class called EmployeeModel.cs and add the code below to it.

    public record Employee(int Id, string Name, int Age, int DeptId );

public record Department(int Id, string Name);

public class EmployeeDetails
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string DeptName { get; set; }
}

public class EmployeeDetailsType : ObjectGraphType<EmployeeDetails>
{
public EmployeeDetailsType()
{
Field(x => x.Id);
Field(x => x.Name);
Field(x => x.Age);
Field(x => x.DeptName);
}
}

EmployeeDetails is the model for API response but GraphQL can’t understand this hence we need to create a mapping and as a result, we create EmployeeDetailsType Field mapping class.

Step 3: Create the Employee Service class, which caters to the data from the data source. For this example we hardcode. Create the EmployeeService.cs class and add the below code.

    public interface IEmployeeService
{
public List<EmployeeDetails> GetEmployees();

public List<EmployeeDetails> GetEmployee(int empId);

public List<EmployeeDetails> GetEmployeesByDepartment(int deptId);
}

public class EmployeeService : IEmployeeService
{
public EmployeeService()
{

}
private List<Employee> employees = new List<Employee>
{
new Employee(1, "Tom", 25, 1),
new Employee(2, "Henry", 28, 1),
new Employee(3, "Steve", 30, 2),
new Employee(4, "Ben", 26, 2),
new Employee(5, "John", 35, 3),

};

private List<Department> departments = new List<Department>
{
new Department(1, "IT"),
new Department(2, "Finance"),
new Department(3, "HR"),
};

public List<EmployeeDetails> GetEmployees()
{
return employees.Select(emp => new EmployeeDetails {
Id = emp.Id,
Name = emp.Name,
Age = emp.Age,
DeptName = departments.First(d => d.Id == emp.DeptId).Name,
}).ToList();

}
public List<EmployeeDetails> GetEmployee(int empId)
{
return employees.Where(emp => emp.Id == empId).Select(emp => new EmployeeDetails
{
Id = emp.Id,
Name = emp.Name,
Age = emp.Age,
DeptName = departments.First(d => d.Id == emp.DeptId).Name,
}).ToList();
}

public List<EmployeeDetails> GetEmployeesByDepartment(int deptId)
{
return employees.Where(emp => emp.DeptId == deptId).Select(emp => new EmployeeDetails
{
Id = emp.Id,
Name = emp.Name,
Age = emp.Age,
DeptName = departments.First(d => d.Id == deptId).Name,
}).ToList();
}
}

The above code is self-explanatory hence moving to the next step.

Step 4: Here we will create the GraphQL Query which is the key for GraphQL APIs. Add a class EmployeeQuery.cs and the below code to it.

    public class EmployeeQuery : ObjectGraphType
{
public EmployeeQuery(IEmployeeService employeeService) {
Field<ListGraphType<EmployeeDetailsType>>(Name = "Employees", resolve : x => employeeService.GetEmployees());
Field<ListGraphType<EmployeeDetailsType>>(Name = "Employee",
arguments: new QueryArguments(new QueryArgument<IntGraphType> { Name = "id"}),
resolve: x => employeeService.GetEmployee(x.GetArgument<int>("id")));
}
}

public class EmployeeDetailsSchema : Schema
{
public EmployeeDetailsSchema(IServiceProvider serviceProvider) : base(serviceProvider) {
Query = serviceProvider.GetRequiredService<EmployeeQuery>();
}
}

Here we do two things, One, we create the GraphQL Query (i.e EmployeeQuery) mapping against our EmployeeService methods to fetch data and the syntax is:
Field<ListGraphType<{modelmappingtype class}>(Name, arguments, resolve);
Arguments are optional if not needed.

In the above code, we have created two query mapping to fetch all employees and employees with employee IDs.

The second part is to map the Employee query (EmployeeQuery) class to the GraphQL schema by creating a class (EmployeeDetailsSchema) that inherits Schema.

Step 5: Register the types and services including GraphQL to the dependency container in Program.cs class.

builder.Services.AddSingleton<IEmployeeService, EmployeeService>();
builder.Services.AddSingleton<EmployeeDetailsType>();
builder.Services.AddSingleton<EmployeeQuery>();
builder.Services.AddSingleton<ISchema, EmployeeDetailsSchema>();
builder.Services.AddGraphQL(b => b
.AddAutoSchema<EmployeeQuery>() // schema
.AddSystemTextJson()); // serializer

Step 6: As part of the last step, we will register the GraphQL endpoint and playground (it is like Swagger) to the application.

app.UseGraphQL<ISchema>("/graphql");            // url to host GraphQL endpoint
app.UseGraphQLPlayground(
"/", // url to host Playground at
new GraphQL.Server.Ui.Playground.PlaygroundOptions
{
GraphQLEndPoint = "/graphql", // url of GraphQL endpoint
SubscriptionsEndPoint = "/graphql", // url of GraphQL endpoint
});

Now we run the application and the output you would see as:

From the right-side tab options, you will be able to see the Schema and the Docs which tell about queries details as:

And this is how we will make the query.

Query 1: Get all Employees with only Employee Name and their Department Name.

Query 2: Combining two queries: Get all Employees with only Employee Name and their Department Name and the employee with employee id as 2.

To know more about the GraphQL.NET library please visit: https://github.com/graphql-dotnet/server

Bonus

You can get the complete code used in this example here: https://github.com/binodmahto/FunProjects/tree/main/GraphQL/graphqlapidemo
This also has the WEB API REST implementation of the same endpoints which we discussed here in case you want to compare the code for REST API and GraphQL API side by side.

Also here is an example to consume the API through HTTP GET and POST.

HTTP GET

We need to pass the json query as part of query string with a syntax ?query= {json query string}

HTTP POST

In case of a POST request, we will follow the syntax for the same above query as:

Monday, 5 December 2022

CI/CD with GitHub Actions pipeline to run the .Net unit test and publish results

 Writing Tests (Unit Tests, Integration Tests) is not only the best practice but also essential to ensure quality code.

From the DevOps side, it is essential to put a gate to check for successful unit test execution with each pull request or push. So in this article, we will see how to implement a pipeline to run the unit tests and publish the results.

What do we need?

  1. We need a pipeline to be triggered with every pull request for your code repo.
  2. Jobs to run the unit tests and publish the results.

Please follow my other articles to learn more about GitHub Actions: CI/CD with GitHub ActionsCI/CD with GitHub Actions to deploy Applications to Azure App ServiceCI/CD with GitHub Actions to deploy Application to Azure Kubernetes Cluster

Let’s create our pipeline for unit test execution. To do this add a yaml/yml file as .github/workflows/buildAndRunTest.yml in the root project folder and then starts with defining the name & triggering action.

name: Build and run tests

env:
DOTNET_VERSION: '6.0' # set this to the .NET Core version to use
WORKING_DIRECTORY: './src' #define the root folder path

on:
workflow_dispatch: #for manual trigger
pull_request: #trigger action with pull request
branches:
- main #your branch name

Next step, we will define the job and mention the target machine (Linux/windows) then perform the necessary steps for code checkout and setting .net core

jobs:
build:
runs-on: ubuntu-latest #target machine is ubuntu

steps:
- uses: actions/checkout@v2

- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ env.DOTNET_VERSION }} #reading it from env variable defined above

I’m using “EnricoMi/publish-unit-test-result-action” action from the marketplace with JUnit logger hence we need to install the related package for each test project as the next step.

Xml logger for JUnit v5 compliant xml report when test is running with “dotnet test” or “dotnet vstest”.

- name: Setup test loggers
shell: pwsh
run: |
foreach ($file in Get-ChildItem -Path .src/tests/*Tests.csproj -Recurse)
{
dotnet add $file package JunitXml.TestLogger --source 'https://api.nuget.org/v3/index.json'
}

From the above code, looping through all test projects inside the src/tests folder.

Next step we will execute the tests with dotnet command.

- name: Run unit tests
shell: bash
working-directory: ./src
run: |
dotnet test myDemoApp.sln --logger 'junit;LogFileName=TestResults.xml' \
--verbosity normal --results-directory ./_test-results

The above code will run all the unit tests in the project and save the result inside the src/_test-results/TestResults.xml file.

If you have categorized tests then you can use the filter option with dotnet test as:
dotnet test — filter “TestCategory=CategoryA

Finally will publish the results using “EnricoMi/publish-unit-test-result-action” from the marketplace.

 - name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
junit_files: ./src/_test-results/*.xml

We are done so With this if you the actions pipeline you will see the results as:

Note: There are various actions available in the GitHub marketplace to publish your test results, please explore them too.

Hope you enjoyed the content, follow me for more like this, and please don’t forget to LIKE  it. Happy programming.