GraphQL is a versatile query language that not only facilitates data retrieval but also enables data modification through a feature known as mutations. GraphQL Mutations in Expressjs are operations in GraphQL that allow you to create, update, or delete records on the server.
In our previous example, the GraphQL schema consisted of a single root query, granting access to the courseType
and studentType
. Now, we aim to set up our mutations in a similar fashion to the types we’ve previously learned.
To achieve this, we extend our schema by introducing a mutation object. This mutation object becomes the focal point for operations that involve adding, editing, or deleting courses within the course collection. All manipulative operations are encapsulated within this mutation object.
We must instruct the mutation object on how to interact with and manipulate the data in our application. This involves defining specific mutation types for adding, editing, and deleting courses, outlining the input parameters required for these operations.
By structuring our mutations in this manner, we maintain a coherent and organized GraphQL schema, where the mutation object serves as the hub for all data manipulation activities. Developers can then leverage these mutations in their queries to seamlessly perform various operations on the course collection, ensuring a consistent and controlled approach to data modification within our application.
Table of Contents
Step 1: Define the Mutation Object in schema.js
File
To employ mutations effectively, the first step is to define a mutation operation within your GraphQL schema. This operation outlines the changes you intend to make to the data and specifies the fields that will be returned as a result of the mutation. This structured approach ensures clarity in the modification process.
In our example, we are defining the mutation object in the GraphQL schema to add a new course. It involves specifying the mutation name, its return type, and the required arguments. Additionally, we introduce validation for mandatory fields.
const mutation = new GraphQLObjectType({
name: "Mutation",
fields: {
addCourse: {
type: CourseType,
args: {
courseName: { type: new GraphQLNonNull(GraphQLString) },
description: { type: new GraphQLNonNull(GraphQLString) },
// courseId is optional, as it may be generated by the server
courseId: { type: GraphQLString },
},
resolve(parentValue, { courseName, description }) {
// Destructure the arguments and perform the operation
return axios
.post("http://localhost:3000/courses/", { courseName, description })
.then((resp) => resp.data);
},
},
},
});
In this example, addCourse
mutation is added to create a new course. The GraphQLNonNull
ensures that required fields (courseName
and description
) must be provided. The resolve
function is where the actual operation of creating a new record is performed.
Once you’ve defined the mutation operation, you can incorporate it into a GraphQL query. Within the query, you’ll identify the mutation operation you wish to execute and provide the necessary arguments to convey the data modifications. These arguments contain the details required for creating, updating, or deleting records.
When the mutation is executed, it initiates the specified modifications to the server’s data based on the arguments provided in the query. Subsequently, the mutation returns the fields outlined in the mutation operation, offering confirmation that the modification was successful. This feedback mechanism allows you to verify and handle the outcome of the mutation effectively.
Step 2: Associate Mutation with GraphQL Schema
Once the root mutation object is defined, it needs to be associated with the overall GraphQL schema.
module.exports = new GraphQLSchema({
query: RootQuery,
mutation: mutation,
});
By including the mutation in the schema, you enable the execution of mutation operations alongside queries.
Calling Mutation Syntax in Browsers
When calling the mutation to add a new record, the syntax differs slightly from the queries.
mutation {
addCourse(courseName: "New Course", description: "Description of the new course") {
id
courseName
description
}
}
This mutation syntax allows you to specify the input parameters (courseName
and description
) and request specific fields in the response. The result would be the details of the newly added course. Here is the link where we add above code in browser.http://localhost:4000/graphql?query=
Example 2 Adding Delete operation in mutation object
Let add another example to delete course operation in mutation object in schema.js file
const mutation = new GraphQLObjectType({
name: "Mutation",
fields: {
addCourse: {
type: CourseType,
args: {
courseName: { type: new GraphQLNonNull(GraphQLString) },
description: { type: new GraphQLNonNull(GraphQLString) },
// courseId is optional, as it may be generated by the server
courseId: { type: GraphQLString },
},
resolve(parentValue, { courseName, description }) {
// Destructure the arguments and perform the operation
return axios
.post("http://localhost:3000/courses/", { courseName, description })
.then((resp) => resp.data);
},
},
deleteCourse: {
type: CourseType,
args: {
id: { type: new GraphQLNonNull(GraphQLString) },
},
resolve(parentValue, args) {
// Send a DELETE request to the specific course endpoint
return axios
.delete(`http://localhost:3000/courses/${args.courseId}`)
.then((resp) => resp.data);
},
},
},
});
In browser let called deleteCourse on mutation objects
mutation {
deleteCourse(id: "2") {
id
}
}
This mutation is requesting to delete a course with the ID of “2” and expecting to receive the ID of the deleted course in the response.
Example 3 Edit operation on mutation object
Let’s add an edit operation to the mutation object. In RESTful terms, we commonly use PUT and PATCH requests for editing records. The distinction lies in how they handle updates.
- PUT Request: This method is used when we want to completely replace the existing record with the new data provided in the request body. All properties not present in the request body are typically set to null or default values.
- PATCH Request: On the other hand, the PATCH request is employed when we want to selectively update or overwrite specific properties within the existing record. Only the properties contained within the request body are modified, leaving the rest of the record unchanged.
In the context of GraphQL mutations, the choice between PUT and PATCH is more implicit and handled by the resolver function. GraphQL itself does not dictate the use of specific HTTP methods, and the focus is on expressing the intent of the operation.
Let’s update our editCourse
mutation in the GraphQL schema accordingly:
....
editCourse: {
type: CourseType,
args: {
id: { type: new GraphQLNonNull(GraphQLString) },
courseName: { type: GraphQLString },
description: { type: GraphQLString },
},
resolve(parentValue, { id, courseName, description }) {
// Send a PATCH or PUT request to update the specific course
return axios
.patch(`http://localhost:3000/courses/${id}`, { courseName, description })
.then((resp) => resp.data);
},
},
....
In this updated code, the editCourse
mutation sends a PATCH request to selectively update the specified properties (courseName
and description
) for the course identified by its id
. This aligns with the behavior of a PATCH request in RESTful terms.
Here's the breakdown of your mutation:
mutation {
editCourse(id: "10", courseName: "Python") {
id
courseName
}
}
Conclusion
In summary, mutations in GraphQL offer a powerful mechanism for controlled and efficient data modification on the server. The ability to define mutation operations within the schema and seamlessly integrate them into queries provides a robust foundation for managing data in a flexible and developer-friendly manner. As developers leverage GraphQL, they can easily craft mutation operations tailored to their application’s requirements, ensuring a dynamic and responsive data management system