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.