Managing Mutation State: Updating Arrays in GraphQL Resolvers
Learn how to perform in-memory state management for your GraphQL API. Discover how to push, update, and return array data within your mutation resolvers.

Previously in this course, we covered implementing a simple mutation and the use of input types to structure our data. While those lessons focused on individual object updates, real-world APIs frequently deal with collections. In this lesson, we’ll move beyond single objects to handle state management for arrays directly in your server's memory.
Why In-Memory State Matters
When you're building a GraphQL server, your resolvers act as the bridge between your schema and your data source. While production systems eventually use databases, keeping data in memory is the perfect way to master the mechanics of GraphQL mutations without the overhead of database drivers or connection pools.
In-memory state management simply means holding an array in a variable within your server file. When a mutation is triggered, we modify that array and return the updated collection. This gives the client immediate feedback that the underlying data has changed.
Working Example: Adding to a List
Let's extend our project. Suppose our API manages a list of "Tasks." We want a mutation that adds a new task and returns the full list of all tasks.
First, define your in-memory array at the top level of your resolvers.js:
JAVASCRIPTlet tasks = [ { id: "1", title: "Learn GraphQL", completed: false }, { id: "2", title: "Build a Server", completed: false } ]; const resolvers = { Mutation: { addTask: (_, { input }) => { const newTask = { id: String(tasks.length + 1), ...input }; // Update the state in memory tasks.push(newTask); // Return the updated collection return tasks; } } };
In your typeDefs, ensure your mutation signature matches the expected return type:
GraphQLtype Mutation { addTask(input: TaskInput!): [Task!]! }
The Mechanics of Array Mutation
When the addTask resolver executes, it performs three distinct steps:
- Creation: It receives the input, generates a unique ID, and constructs the new object.
- Mutation: It uses
tasks.push()to modify the reference of ourtasksarray. - Synchronization: By returning the entire
tasksarray, we ensure the client-side cache receives the full, current state of the collection.
If you are curious about how this compares to more complex client-side patterns where state is handled in the UI, you can read more about managing state arrays in React to see how the frontend consumes this data.
Practice Exercise
Take your current project and implement a clearTasks mutation.
- Define a
clearTasksmutation in yourtypeDefsthat returns an empty array[Task!]!. - In your resolvers, update the
tasksarray to be an empty array[]. - Verify the operation by running the mutation in Apollo Sandbox and checking that your
tasksquery returns an empty list immediately after.
Common Pitfalls
- Reassigning the variable: In JavaScript,
tasks = []inside a resolver function might not work if you declaredtasksas aconst. Always useletfor data that needs to be mutated. - Returning the wrong type: If your schema defines
[Task!]!, your resolver must return an array. Returning a single object ornullwill cause a runtime error in GraphQL. - Identity mutations: When updating an array, remember that JavaScript objects are passed by reference. If you modify an object inside the array, you are modifying the source of truth, not a copy. This is usually desired for simple servers but can lead to bugs in complex applications. For a deeper dive into safe update patterns, review how to handle immutable object updates.
FAQ
Q: Will my data persist if the server restarts?
A: No. In-memory state is tied to the lifecycle of the Node.js process. Once you stop the server, the tasks array is wiped.
Q: Is it better to return the single object or the whole list? A: It depends on your UI needs. Returning the single object is faster for the server; returning the whole list is easier for the client to sync if they don't have a sophisticated caching mechanism.
Recap
Managing mutations in memory provides a clear view of how GraphQL resolvers manipulate data. By updating an array and returning the result, you maintain data integrity during the request lifecycle. Remember: keep your state declarations at the top level and ensure your resolver return types match your schema definitions.
Up next: We will refine our approach by learning about mutation naming conventions and why returning the mutated object is standard practice for cache updates.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

Headless WordPress + Next.js Frontend Development
Keep WordPress for content, get a lightning-fast Next.js frontend. The best of both worlds — familiar editing, modern speed.


