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:

Wednesday, 26 April 2023

CI/CD with GitHub Actions to deploy background jobs as Azure WebJobs

 A background job is a task that we need to run in the background like a scheduled job and the Azure WebJobs provides this capability as part of the Azure App Service.

Azure WebJobs is the feature of Azure AppService where you can run your background tasks along with your web application deployed in App Service with NO additional cost.

To know more about WebJobs please read through this article here: https://learn.microsoft.com/en-us/azure/app-service/webjobs-create

In this article, we will talk about how to automate the background task deployment to the Azure AppService WebJob along with the web application. Please note that you can also deploy only the background job as a web job in the Azure app service too.

If you would have followed me on my previous article about automating the web application deployment to the Azure AppService here “CI/CD with GitHub Actions to deploy Applications to Azure App Service” then you have almost got this to 90%. Yes, it is 90% because the deployment and the process are all the same and for rest 10% all you have to do is, publish your background tasks to a specific folder along with your web application.

Note: If you are hosting the web application along with web job then you must have to host it together in a single pipeline as I’ll be showing here or else the wwwroot folder will get overrriden with lastest deployment and this happens because everything goes into the wwwroot folder. Web application content will got directly to the wwwroot folder and background task will insided wwwroot/App_Data/Jobs/Triggered/{your Job Name}

For this example, I’ve got a simple .net console application as a background task which I’m going to deploy as a web Job.

To achieve this, We need to modify Step 7 from my previous article to prepare the app settings. In my case, I’m adding one more execution step in the pipeline as I’m deploying the web application and web job both. So the Step 7 code will finally be as:

In the above code, I added the extra step in the build job to modify the connection string of the background task “DemoTask” which is a console application for me inside a folder called “webjob” from my code repo.

Next, we will modify Step 8 from my previous article, to add a new step to build and publish the console application as a background task i.e. web job. Here is the modified step 8:

In the above code, if you observed, the background task is being published in a specific folder as “${{env.DOTNET_ROOT}}/myapp/App_Data/Jobs/Triggered/${{ env.webJobName }}”

So this is the only catch that, your background task must be in App_Data/Jobs/Triggered/{job_name} inside wwwroot which is the base path.

Thats all. We are done and this is how the folder structure will look like in Azure App Service.

To configure your background task schedule and all, either you can do it directly from the portal Or you can do it through code using web job sdk. Check out the details here: https://learn.microsoft.com/en-us/azure/app-service/webjobs-sdk-how-to

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