Introduction
As we learned from our previous article, GraphQL helps us to.
- Have one request instead of multiple
- Have one endpoint instead of multiple
- Avoid versioning
- To get exactly what we want, instead of having "ALL"
- Avoid over-fetching and under-fetching
- Being mobile-friendly rather than desktop one
- To do fast iteration instead of waiting for backend guys to add/remove some fields
So, the purpose of the current article is actually to dive into the details of Quering in the GraphQL environment. We'll learn what Query is, how to deal with that, and a little bit about resolvers.
Getting started with GraphQL
Unlike REST, GraphQL doesn't have many verbs to learn.
They are Three
- Query: Equal to HTTP GET in REST
- Mutation: Equivalent to POST, PUT, DELETE, PATCH
- Subscription: No REST equivalent
Note. GraphQL is a query language for our API and uses type-oriented modeling to query or mutate data.
It is strongly typed, and to build a query mechanism against GraphQL, we need to have a type schema.
Step 1. Create a new ASP.net core WebAPI project.
Step 2. Call it "IntegratingGraphQL".
Step 3. Install hotchocolate package using "install-package hotchocolate.aspnetcore"
Step 4. Add a class called Query with the following code.
namespace IntegratingGraphQL
{
public class Query
{
public string GetName()
{
return "Simon Baker";
}
}
}
Step 5. Go to the Program.cs and make the following changes.
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddGraphQLServer()
.AddQueryType<Query>();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.MapGraphQL();
app.Run();
}
As we learned before, to handle graphQL queries, we need to build a GraphQL server. ( AddGraphQLServer)
To define the exact type, we need to provide our class as a query type ( AddQueryType<Query>). To run and map queries, we need to use MapGraphQL() middleware.
When you run the application using /graphQL route, we will get the following page.
Click "Create document" and navigate to "Schema Reference".
In our schema, we have one single Query object with a "name" field.
It is our "GetName()" method with a slightly modified version.
HotChocolate can remove verbs from the prefix. So, it automatically removes "GET" or other verbs from the "field".
Let's query and see if everything is working.
Why did I call it a field? Because they are pure properties/fields from the GraphQL perspective. Every method inside a Query class will be transformed into fields.
But why do we create methods inside a Query?
Because these methods act as a resolver. C# methods are resolvers, and they help us to resolve the field we need to access.
Let's extend our Query class with the following code.
namespace IntegratingGraphQL
{
public class Query
{
public string GetName()
{
return "Simon Baker";
}
public IEnumerable<Person> GetPersons()
{
return new List<Person>
{
new Person(1,"Hanma Baki"),
new Person(2,"Hanayama Kaoru"),
new Person(3,"Oroti Doppo")
};
}
}
public record Person(int Id, string Name);
}
Now, we have a complex data type called Person, and our method returns a list of them. In the above case, all methods are resolvers.
Let's move forward and reload our schema.
Now, graphQL will be capable of reloading schema and the resolver's field.
This "annotation-based approach" helps us dive into the details of scheme definition and "extract" required type models from the C# code.
In general, it is a piece of cake for you to use GraphQL with hotchocolate even not diving into type details. But in the future,
If you need to adapt more GraphQL endpoints, you will face issues where you need to have a good type understanding.
Here is our request and response from the GraphQL server.
GraphQL, in its nature, has parallel querying ability. Suppose you want to call both resolvers at the same time. You can easily do it.
Request
{
name
persons {
name
}
}
Response
{
"data": {
"name": "Simon Baker",
"persons": [
{
"name": "Hanma Baki"
},
{
"name": "Hanayama Kaoru"
},
{
"name": "Oroti Doppo"
}
]
}
}
GraphQL operations
GraphQL, by its syntax, is all about operations. Every request to the server is called "Operations". You can do Query, Mutation, and Subscription operations. You may ask where the operation is in the above examples.
Well, root curly brackets are our operation in that case. Because GraphQL can simplify querying, it allows us not to directly apply the operation syntax. We mostly use operation syntax.
- For Query where we want to introduce some variable(s)
- Detailed logging. To log the exact query name
- To define the same endpoint queries with different fields
The query
{
name
persons {
name
}
}
Is equal to
query {
name
persons {
name
}
}
This is equal to
query Simplequery{
name
persons {
name
}
}
A bit more detail
GraphQL is transport-agnostic in nature. It means you can use HTTP, WebSockets, and even other transportation mechanisms to interact with the graphQL server. By default, graphQL uses HTTP POST for both Query and Mutation. GraphQL Subscription mostly uses WebSockets. But it is possible to use server-side events for them also.