26 What Is a Mutation and When Should You Use It?
title: “26 What Is a Mutation and When Should You Use It?” date: 2024-06-15 author: “Seno Pratama” tags: [Mutation, State Management, GraphQL, Engineering Best Practices]
26 What Is a Mutation and When Should You Use It?
If you build modern applications—especially on the frontend with frameworks like React, or with GraphQL on the backend—the term “mutation” is certainly nothing new. Yet for some engineers, the concept of a mutation often remains fuzzy, treated as just a vague “change in data.” This article breaks it down completely: what a mutation is, when to use it, and what best practices look like—complete with code examples and flow diagrams.
What Is a Mutation?
Simply put, a mutation means a change in data. Unlike a query, which is read-only, a mutation refers to an operation that modifies, adds, or deletes data in the system.
In short:
- Query => “Give me this data!”
- Mutation => “Please change this data!”
The concept of mutation appears widely in state management patterns on the frontend (such as Redux, Recoil, Zustand) as well as in modern APIs like GraphQL.
Mutations in State Management
Let’s start with the case of state management in frontend applications.
Immutable vs Mutable
In the reactive or functional programming paradigm, state changes are expected to be immutable—meaning you don’t “modify” the old value, but instead create a new copy with the desired changes.
A mutation (modifying data directly) looks like this:
1// Mutable: Not recommended in React!
2const user = { name: "Seno", age: 30 };
3user.age = 31;
Whereas immutability is done like this:
1// Immutable: Recommended in React/Redux
2const user = { name: "Seno", age: 30 };
3const updatedUser = { ...user, age: 31 };Why must it be immutable?
- Predictability: State changes are easier to track.
- Easier Debugging: Time-travel debugging becomes possible.
- Optimization: React and friends can compare states more quickly.
Mutations in Redux With Reducers
Redux, one of the most popular state managers in the JavaScript ecosystem, places heavy emphasis on using immutability. Here’s an example of implementing a data mutation using a Redux-style reducer:
1function todosReducer(state = [], action) {
2 switch (action.type) {
3 case "addTodo":
4 // Use spread to add, never modify directly
5 return [...state, action.payload];
6 case "removeTodo":
7 // Filter creates a new array without modifying state
8 return state.filter(todo => todo.id !== action.payload.id);
9 default:
10 return state;
11 }
12}In the example above, every “mutation” to state creates a new snapshot so the state remains predictable. This is crucial to keep the application stable and easy to debug.
Mutations in GraphQL
In a different context, a mutation in GraphQL is one of the operation types, alongside query and subscription.
GraphQL Mutation Structure
1mutation {
2 addBook(title: "Belajar GraphQL", author: "Seno") {
3 id
4 title
5 author
6 }
7}GraphQL Mutation Flow Diagram
flowchart TD
C(Client) -->|Send Mutation| API[(GraphQL API)]
API -->|Validate| R{Resolver}
R -->|Ubah/Entri Data| DB[(Database)]
DB --> R
R --> API
API -->|Return Result| C
Explanation:
- The client sends a mutation request to the GraphQL API.
- The API performs validation, then runs the dedicated mutation Resolver.
- The Resolver modifies the data in the database.
- The final result is returned to the client.
Example: Mutation on an Express + Prisma Backend
Suppose you build a user register API:
GraphQL Schema
1type Mutation {
2 registerUser(username: String!, password: String!): User
3}
4
5type User {
6 id: ID!
7 username: String!
8}Handler in Node.js (Express + Prisma)
1const resolvers = {
2 Mutation: {
3 registerUser: async (_parent, args, context) => {
4 const user = await context.prisma.user.create({
5 data: {
6 username: args.username,
7 password: hashPassword(args.password),
8 },
9 });
10 return user;
11 },
12 },
13};When Should You Use a Mutation?
1. When You Want to Change State (State Management)
For example: modifying, deleting, or adding a value to an array/object in frontend state.
2. When You Need to Update Data on the Server (API/GraphQL)
Any request that:
- Adds data (
create) - Modifies data (
update) - Deletes data (
delete)
should use a mutation rather than a query.
3. Triggering a Side Effect
For example, after a user’s data changes, we need to send an email or notification automatically.
Simulation: Frontend-Backend Mutation Flow
Let’s simulate the flow from frontend to backend using a GraphQL mutation:
- The user clicks “Add Book” in the application.
- The frontend sends a mutation:
1mutation { 2 addBook(title: "GraphQL 101", author: "Seno") { 3 id 4 title 5 author 6 } 7} - The backend receives it, adds it to the database, then responds with the new data.
- The frontend receives the response and updates the UI.
Query vs Mutation Comparison Table
| Feature | Query | Mutation |
|---|---|---|
| Purpose | Fetch data (read) | Modify/add/delete data (write) |
| Side Effects | None (idempotent) | Yes (changes the system) |
| Response | Similar to GET in REST | Similar to POST/PUT/DELETE in REST |
| Example | getUser(id: 1) | updateUser(id: 1, name: "Seno") |
Best Practices for Using Mutations
- Always Use Immutability (Frontend State) Never modify objects or arrays directly; always create a copy.
- Test Data Mutations in Isolation Unit tests are very useful for making sure mutation logic runs correctly.
- Avoid Side Effects in Reducers/Inside Mutations As much as possible, keep mutations (especially in Redux) pure, without running other effects.
- Use Clear Naming
Such as
addTodo,editProfile, notdoSomething. - Audit & Logging in Backend Mutations Every important data change should be recorded so it’s easy to trace when data goes wrong.
Conclusion
Mutation is a fundamental concept both in frontend state management and in modern APIs like GraphQL. By understanding what a mutation is and when to use it, engineers can build applications that are more structured, easier to maintain, and ready to scale to a larger size.
My advice: get into the habit of using an immutable approach when changing state, and use mutations explicitly when modifying data on an API. That way bugs are easier to find, and your application is more durable!
Do you have an interesting experience or challenge around using mutations in applications? Share it in the comments!