Preforming CRUD Operations Using GraphQL In ASP.NET Core

Introduction

 
Like REST, GraphQL also allows the client to make changes to data that’s currently stored in the backend. In GraphQL, these changes are made using mutations which is nothing but a type of operation that allows creating, updating, and deleting data. For the read operation, we used GraphQL query type (explained in the article “Developing API in .NET Core with GraphQL”), which is completely flexible, and based on the requirement that the user/client decides what data is actually needed.
 
As we know, GraphQL specifies three types that are used to perform operations of a GraphQL server.
  • Query - Used for retrieving data based on needs.
  • Mutation – Used for making changes to the data/entity.
  • Subscription – Used for real-time updates on important events.
Related reads:

Implementing GraphQL Mutation with ASP.NET Core

 
In this article, we will be going to add a mutation type that allows users to modify a tech event entity into the DB. This article is a continuation of the previous one which is mentioned in related reads.
 
First, we're going to make some changes to model and repositories which will allow inserting, updating, and deleting on tech event entity. Next, we're going to create new GraphQL types that we need in order to enable the mutation. And then we will update the schema so that mutation will be available to the client/user and then create a mutation and test is though GraphiQL UI as well as Postman.
 
Create models and add methods to the repository
 
Add a folder called Models into the project (which we build earlier here) and add a new model called NewTechEventRequest.cs into it which basically contains properties that are required to create a new tech event and save into DB.
 
NewTechEventRequest.cs

  1. using System;  
  2.   
  3. namespace GraphQL.API.Models  
  4. {  
  5.     public class NewTechEventRequest  
  6.     {  
  7.         public string EventName { getset; }  
  8.         public string Speaker { getset; }  
  9.         public DateTime EventDate { getset; }  
  10.     }  
  11. }  

Now we are going to add the below 3 methods to our repository interface.
 
ITechEventRepository.cs

  1. Task<TechEventInfo> AddTechEventAsync(NewTechEventRequest techEvent);  
  2. Task<TechEventInfo> UpdateTechEventAsync(TechEventInfo techEvent);  
  3. Task<bool> DeleteTechEventByIdAsync(TechEventInfo techEvent); 
  4.  
And implement those methods into TechEventRepository.cs.

  1. public async Task<TechEventInfo> AddTechEventAsync(NewTechEventRequest techEvent)  
  2. {  
  3.     var newEvent = new TechEventInfo { EventName = techEvent.EventName, Speaker = techEvent.Speaker, EventDate = techEvent.EventDate };  
  4.     var savedEvent = (await _context.TechEventInfo.AddAsync(newEvent)).Entity;  
  5.     await _context.SaveChangesAsync();  
  6.     return savedEvent;  
  7. }  
  8.   
  9. public async Task<bool> DeleteTechEventByIdAsync(TechEventInfo techEvent)  
  10. {  
  11.     _context.TechEventInfo.Remove(techEvent);  
  12.     await _context.SaveChangesAsync();  
  13.     return true;  
  14. }  
  15.   
  16. public async Task<TechEventInfo> UpdateTechEventAsync(TechEventInfo techEvent)  
  17. {  
  18.     var updatedEvent = (_context.TechEventInfo.Update(techEvent)).Entity;  
  19.     await _context.SaveChangesAsync();  
  20.     return updatedEvent;  
  21. }
  22.   

Create GraphQL input type

 
Next, we're going to update the schema. To do this, create a GraphQL input type which will correspond with the NewTechEventRequest model that we just created. This is the type that we are going to send from a client as an argument to mutation.
 
Right click on GraphqlCore folder, then add a new class called TechEventInputType.cs and add below-mentioned code.
 
TechEventInputType.cs

  1. using GraphQL.Types;  
  2.   
  3. namespace GraphQL.API.GraphqlCore  
  4. {  
  5.     public class TechEventInputType : InputObjectGraphType  
  6.     {  
  7.         public TechEventInputType()  
  8.         {  
  9.             Name = "AddEventInput";  
  10.             Field<NonNullGraphType<StringGraphType>>("eventName");  
  11.             Field<StringGraphType>("speaker");  
  12.             Field<NonNullGraphType<DateGraphType>>("eventDate");  
  13.         }  
  14.     }  
  15.  
The TechEventInputType class derives from the InputObjectGraphType class and in the constructor, we just populate the Name property and added three fields.
 
Here:
  • InputObjectGraphType – used to define an input type and this is going to tell GraphQL that this is an input type.
  • NonNullGraphType – used to require a non-null values.
  • StringGraphType – used to define a string field.
  • DateGraphType – used to define a date field.
Create Mutation
 
Create the TechEventMutation class inside the GraphqlCore folder.
 
TechEventMutation.cs

  1. using GraphQL.API.Infrastructure.Repositories;  
  2. using GraphQL.Types;  
  3. using GraphQL.API.Models;  
  4. using GraphQL.API.Infrastructure.DBContext;  
  5.   
  6. namespace GraphQL.API.GraphqlCore  
  7. {  
  8.     public class TechEventMutation : ObjectGraphType<object>  
  9.     {  
  10.         public TechEventMutation(ITechEventRepository repository)  
  11.         {  
  12.             Name = "TechEventMutation";  
  13.   
  14.             FieldAsync<TechEventInfoType>(  
  15.                 "createTechEvent",  
  16.                 arguments: new QueryArguments(  
  17.                     new QueryArgument<NonNullGraphType<TechEventInputType>> { Name = "techEventInput" }  
  18.                 ),  
  19.                 resolve: async context =>  
  20.                 {  
  21.                     var techEventInput = context.GetArgument<NewTechEventRequest>("techEventInput");  
  22.                     return await repository.AddTechEventAsync(techEventInput);  
  23.                 });  
  24.         }  
  25.     }  
  26. }  

Inside the constructor, the name is the name of the mutation and field with the name createTechEvent, which will return the TechEventInfoType object.
 
When we create a tech event, we are going to pass an instance of an argument of type TechEventInputType, with a variable name “techEventInput” that we provide in GraphQL. The resolve action is going to execute the AddTechEventAsync method from our repository to add data into DB.
 
TechEventSchema.cs
  1. Mutation = resolver.Resolve<TechEventMutation>();  
Update startup.cs class and register these types into ConfigureServices that we created so far.
  1. services.AddSingleton<TechEventInputType>();  
  2. services.AddSingleton<TechEventMutation>();  
Excellent, now we are done with adding Mutation into our project. Let’s start the project, and create a tech event from GraphiQL. We will see newly added schema details once it’s loaded to the browser.
 
 
Now it’s time to test it.
 
The syntactical structure of Mutations is similar to queries, but always starts with the mutation keyword. Below is the example to create a tech event.
  1. mutation ($techEvent : AddEventInput!){  
  2.   createTechEvent(techEventInput : $techEvent){  
  3.     eventId,  
  4.     eventName,  
  5.     eventDate,  
  6.     participants{  
  7.       phone  
  8.     }  
  9.   }  
  10. }  
Add the below variable to the Query Variables window:
  1. {  
  2.   "techEvent": {  
  3.     "eventName""Azure Devops CI/CD Demo",  
  4.     "speaker""Sumana M",  
  5.     "eventDate""2020-12-12"  
  6.   }  
  7. }  
 
Here, we create a tech event with an argument called techEventInput and return the eventId, eventName, eventDate, participants of that newly added event. We used a variable called “techEvent” to pass in the techEventInput argument via the Query Variables window. On the right side of GraphiQL, we can see that event creation is successful and got the newly created tech event as a response.
 
Let’s test the same using Postman. We will use the below output as an example.
 
 

Update Mutation

 
Whatever we did for Create tech event, we can similarly do for an update tech event.pasting. Add an additional field into TechEventMutation.cs with the name “updateTechEvent”.
  1. FieldAsync<TechEventInfoType>(  
  2.     "updateTechEvent",  
  3.     arguments: new QueryArguments(  
  4.         new QueryArgument<NonNullGraphType<TechEventInputType>> { Name = "techEventInput" },  
  5.         new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "techEventId" }),  
  6.     resolve: async context =>  
  7.     {  
  8.         var techEventInput = context.GetArgument<TechEventInfo>("techEventInput");  
  9.         var techEventId = context.GetArgument<int>("techEventId");  
  10.   
  11.         var eventInfoRetrived = await repository.GetTechEventById(techEventId);  
  12.         if (eventInfoRetrived == null)  
  13.         {  
  14.             context.Errors.Add(new ExecutionError("Couldn't find Tech event info."));  
  15.             return null;  
  16.         }  
  17.         eventInfoRetrived.EventName = techEventInput.EventName;  
  18.         eventInfoRetrived.EventDate = techEventInput.EventDate;  
  19.   
  20.         return await repository.UpdateTechEventAsync(eventInfoRetrived);  
  21.     }  
  22. );  
Now run the project again and test it.
 
 
When we pass a non-existing event during an update operation, we receive an error message which is expected as we defined it.
 
 

Delete Mutation

 
Similarly, add a new field for deleting an operation in the TechEventMutation.cs class and name it as “deleteTechEvent”.
  1. FieldAsync<StringGraphType>(  
  2.     "deleteTechEvent",  
  3.     arguments: new QueryArguments(new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "techEventId" }),  
  4.     resolve: async context =>  
  5.     {  
  6.         var techEventId = context.GetArgument<int>("techEventId");  
  7.   
  8.         var eventInfoRetrived = await repository.GetTechEventById(techEventId);  
  9.         if (eventInfoRetrived == null)  
  10.         {  
  11.             context.Errors.Add(new ExecutionError("Couldn't find Tech event info."));  
  12.             return null;  
  13.         }  
  14.         await repository.DeleteTechEventByIdAsync(eventInfoRetrived);  
  15.         return $"Tech Event ID {techEventId} with Name {eventInfoRetrived.EventName} has been deleted succesfully.";  
  16.     }  
  17. );  
Let’s run the project again and test it with the below mutation request:
  1. mutation ($techEventId:ID!){  
  2.   deleteTechEvent(techEventId : $techEventId)  
  3. }  
Query variable:
  1. {  
  2.   "techEventId": 5  
  3. }  
Awesome, isn’t it! We are done with basic CRUD operations using GraphQL in ASP.NET Core.
 
All the source code shown and described here can be found on GitHub.
 

Conclusion

 
In this article, we discussed the input type for the mutations, created different mutation actions, and performed CRUD operations on an entity by modifying mutation requests and testing it from GraphiQL as well as Postman. I hope you learned GraphQL API implementation with .NET Core using mutation and query type so far. In the next article, we will discuss subscription type. Happy learning!