GraphQL mutations are actions which we use to Add, Update and Delete data from a database. Until now, we have been executing only GraphQL Queries (fetching data) but in this article, we are going to talk more about data mutations in GraphQL.
To download the source code, visit the GraphQL Mutations Source Code.
For the complete navigation of this tutorial visit GraphQL ASP.NET Core Tutorial.
Input Types and Schema Enhancing for the GraphQL Mutations
Let’s start with creating a new class OwnerInputType
inside the Types
folder:
public class OwnerInputType : InputObjectGraphType { public OwnerInputType() { Name = "ownerInput"; Field<NonNullGraphType<StringGraphType>>("name"); Field<NonNullGraphType<StringGraphType>>("address"); } }
This is the type that we are going to send from a client as an argument for our mutations. As we can see, this class derives from the InputObjectGraphType
class and not from the ObjectGraphType
as before.
In the constructor, we just populate the Name
property and create two fields. We can see that we don’t have the Id
and Accounts
properties because we don’t need them for the mutations.
If we can recall the starting article of this series, there we can see that for the queries we had to create the AppQuery
class. Well, it is the same for mutations, just we are not going to create the AppQuery
class but the AppMutation
class inside the GraphQLQueries
folder:
public class AppMutation : ObjectGraphType { public AppMutation() { } }
Finally, we need to enhance our Schema
class, with the Mutation
property:
public class AppSchema : Schema { public AppSchema(IServiceProvider provider) :base(provider) { Query = provider.GetRequiredService<AppQuery>(); Mutation = provider.GetRequiredService<AppMutation>(); } }
Excellent. We are ready to create some mutations in our project.
Create Mutation
Let’s start with the IOwnerRepository
interface modification:
public interface IOwnerRepository { IEnumerable<Owner> GetAll(); Owner GetById(Guid id); Owner CreateOwner(Owner owner); }
We see that the CreateOwner
method returns a newly created owner object, which is quite common in GraphQL.
Now, we can add the CreateOwner
method inside the OwnerRepository
class:
public Owner CreateOwner(Owner owner) { owner.Id = Guid.NewGuid(); _context.Add(owner); _context.SaveChanges(); return owner; }
Finally, we can modify the AppMutation
class:
public class AppMutation : ObjectGraphType { public AppMutation(IOwnerRepository repository) { Field<OwnerType>( "createOwner", arguments: new QueryArguments(new QueryArgument<NonNullGraphType<OwnerInputType>> { Name = "owner" }), resolve: context => { var owner = context.GetArgument<Owner>("owner"); return repository.CreateOwner(owner); } ); } }
So, we create a field to return the OwnerType
object, with the „createOwner“ name, a single argument of the OwnerInputType
type and with the resolve action which is going to execute the CreateOwner
method from our repository.
And that is it.
Let’s start our project, open Playground and send a request:
So, instead of the query keyword, we use the mutation keyword for mutations. And this is the only new difference. We have an argument, a call to the createOwner
mutation and the fields that we require as a part of the result. On the Playground’s right side, we can see that the creation has been successful and we have a new Owner
object returned.
Awesome, now we can continue on.
Update Mutation
As we did in a previous section of this article, we are going to start with IOwnerRepository
modification:
public interface IOwnerRepository { IEnumerable<Owner> GetAll(); Owner GetById(Guid id); Owner CreateOwner(Owner owner); Owner UpdateOwner(Owner dbOwner, Owner owner); }
Let’s move on to the OwnerRepository
file:
public Owner UpdateOwner(Owner dbOwner, Owner owner) { dbOwner.Name = owner.Name; dbOwner.Address = owner.Address; _context.SaveChanges(); return dbOwner; }
Lastly, we have to add an additional field in a constructor of the AppMutation
class:
Field<OwnerType>( "updateOwner", arguments: new QueryArguments( new QueryArgument<NonNullGraphType<OwnerInputType>> { Name = "owner" }, new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "ownerId" }), resolve: context => { var owner = context.GetArgument<Owner>("owner"); var ownerId = context.GetArgument<Guid>("ownerId"); var dbOwner = repository.GetById(ownerId); if (dbOwner == null) { context.Errors.Add(new ExecutionError("Couldn't find owner in db.")); return null; } return repository.UpdateOwner(dbOwner, owner); } );
Excellent. It is time to test this:
Everything is working as it supposed to.
Let’s move on.
Delete Mutation
Following the same pattern, let’s modify the interface first:
public interface IOwnerRepository { IEnumerable<Owner> GetAll(); Owner GetById(Guid id); Owner CreateOwner(Owner owner); Owner UpdateOwner(Owner dbOwner, Owner owner); void DeleteOwner(Owner owner); }
Then, let’s continue with the OwnerRepository
modification:
public void DeleteOwner(Owner owner) { _context.Remove(owner); _context.SaveChanges(); }
The last thing we need to do is to modify AppMutation
file:
Field<StringGraphType>( "deleteOwner", arguments: new QueryArguments(new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "ownerId" }), resolve: context => { var ownerId = context.GetArgument<Guid>("ownerId"); var owner = repository.GetById(ownerId); if (owner == null) { context.Errors.Add(new ExecutionError("Couldn't find owner in db.")); return null; } repository.DeleteOwner(owner); return $"The owner with the id: {ownerId} has been successfully deleted from db."; } );
And let’s test this as well:
You can send a query to fetch all the owners and see for your self that this owner entity is not in the database anymore.
Conclusion
There we go, all the mutations are completed now.
Now we know, how to use Input type files for the mutations, how to create different mutation actions, and how to create mutation requests from a client side.
In the next part of this tutorial, we are going to create an ASP.NET Core Web API application, to consume the GraphQL application that we created until now.
So, see you there.
Hi,
How can we apply validation with for example fluentvalidation, please?
Sanyi
@@disqus_GtEeIZUUDN:disqus I’m looking to use GraphQL in an ASP.NET Web API application. We plan on all queries being handled by GraphQL. Our UI is Angular.
All POST and PUT operations need to be handled by Web API controllers because our business logic and workflows are super complex, which is why we don’t want to use GraphQL Mutations. Actually, we will be using MediatR for all commands.
What is your recommendation for POST and PUT operations that would typically return an update object back to the caller after the database insert or update has taken place.
I’m asking because GraphQL removes the need for many DTO objects in the code base. So we would have to create DTO objects for each POST and PUT to return. Or, do you recommend that after a POST or PUT, the Angular application use GraphQL to requery the data if required.
Thank you very much. Really like the Ultimate ASP.NET Core 3 Web API.
Karl
Hello Karl. First of all, I am so glad you are enjoying our book, we really invested a lot of time and knowledge into it. So, hearing something like that from the reader is always the best thing.
About your question. My personal opinion is if you don’t have to use more than one request to accomplish something, don’t do it. That said, if you are using Web API to handle these POST and PUT requests, I would create required DTOs and send them back in a same request. Again, this is what I would do if I had to mix GraphQL and Web API logic (but I never did that 😀 ).
@disqus_GtEeIZUUDN:disqus thank you. Great news, I’ve learned the Mutations and added them to my project. Way awesome! Thank you for your guidance and coaching. I’ll post a link to my blog post when I publish it.
Best regards,
Karl
Amazing content! Thank you for these examples. They helped a lot! Official documentation only includes the “create” part.
You are very welcome. I’m glad you enjoyed it. I hope you will find more good stuff on our site. Best regards and see you again 🙂
Thanks very much for your constribution. It saves me a lot of time for my learning curve.
Respectfully yours.
Tam
Thank you too mate. Have a great day. Best regards.