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:
- The Query is complex i.e. https://localhost:7025/graphql?query={employee(id:2){name,deptName}}
- Clients decide what data to get back as a response.
- Allow combining multiple resource queries into one API call. i.e. https://localhost:7025/graphql?query={employee(id:2){name,deptName}, department(id:1){deptName}}. This API query results in an employee with id=2 as well as the department with department id = 1.
- It supports caching too but not as easy because of query behavior.
- It requires heavier tooling support, both on the client and server sides. This may not be suitable for simple CRUD operations.
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:
Hope you enjoyed the content, follow me for more like this, and please don’t forget to clap for it. Happy programming.