GraphQL Mutations Part 2: updateMany, deleteMany, and Re-ordering with Prisma

This post is part 2 on GraphQL Mutations. We’ll be covering how to update and delete multiple records at a time.

Starting Github repository (use start-update-deletemany branch) Note: If you use the github repo you need to switch to the correct branch BEFORE running the npx prisma migrate commands otherwise migration issues will happen.

Learn to code with our beginner friendly tutorials on programming, Linux, Docker, and more

Let’s start by adding a model to the database that would be good for updating and deleting multiple records at a time. I think a TODO item fits the bill here.

We’re going to blaze through the stuff we already covered in previous tutorials.

First let’s setup the database model in schema.prisma. We’re going to make the “order” field optional by adding a ? at the end of the type.

model Todo {
    id          Int     @id @default(autoincrement())
    name        String
    isCompleted Boolean
    order       Int?
}

Now update the database with our 3-command package deal:

$ npx prisma migrate save --name "add-todo-model" --experimental
$ npx prisma migrate up --experimental
$ npx prisma generate

Now we’ll add our Todo type and our createTodo definition in our schema.graphql file:

type Mutation {
  // ...
  createTodo(name: String!, isCompleted: Boolean!, order: Int): Todo!
}

type Todo {
  id: ID!
  name: String!
  isCompleted: Boolean!
  order: Int
}

In our index.js file inside the Mutation:

createTodo: (parent, args, context, info) => {
  return context.prisma.todo.create({
    data: {
      name: args.name,
      isCompleted: args.isCompleted,
      order: args.order,

    },
  });
},

That should allow us to create Todos. Restart the server and create 3 todos just to make sure things are working.

Now let’s setup the query for all todos. Add the todos query to your schema.graphql

type Query {
  //...
  todos: [Todo!]!
}

inside the query root type of the resolvers constant in index.js:

const resolvers = {
  Query: {
    //...,
    todos: (parent, args, context, info) => {
      return context.prisma.todo.findMany();
    },
  }

From here you should be able to run the todos query to see all your todos:

Now we’re ready to work on deleteMany

DeleteMany in GraphQL & Prisma

Deleting many records in one swoop isn’t that much harder. All we have to do is pass in an array of ids. In the Prisma Docs CRUD page it explains that deleteMany returns a “batchPayLoad” which is essentially a COUNT of the objects deleted.

Because we’re deleting all the ID’s that are in the array, we need to have some way for Prisma to look through the array. The Prisma Filtering docs page has information about a where clause which has an in filtering condition that allows us to look through a list. That’s all we need so let’s get to it.

In the “type Mutation” in schema.graphql, add your deleteTodos definition. Also add your “type BatchPayload”:

type Mutation {
//...
  deleteTodos(todoIds: [ID!]!): BatchPayload!
}

type BatchPayload {
  count: Int!
}

Nothing special. We’re just saying the todoIds parameter is a list of ID’s, and it returns an integer (count) via the “BatchPayLoad” type.

Now lets define our Mutation resolver for deleteTodos. Edit your index.js file (put this in const resolvers – Mutation):

deleteTodos: (parent, args, context, info) => {
  let newIds = args.todoIds.map((id) => {
    return parseInt(id);
  });
  return context.prisma.todo.deleteMany({
    where: {
      id: {
        in: newIds,
      },
    },
  });
},

The only new thing here is the Where clause in the deleteMany. The “Where” clause has a few conditionals, “in” being one of them. So “in” looks through the list of newIds and deletes any record where the id is equal to an element in that array.

As of now, Prisma is returning strings, but Int values are required, so we’re converting the array to ints before using them in deleteMany.

Now let’s refresh our server and try it out in the playground. Create a few todos, get their IDs, and run the below query in your Playground:

mutation {
  deleteTodos(todoIds: [7, 8, 9]) {
    count
  }
}

Now go back to your “todos” tab to query for all your todos. The ones that you put in the deleteTodos should be gone.

updateMany

From what we know by now, updateMany is pretty easy. In this example, we’ll reset all the todos checked values. Add the resetTodos schema definition to our schema.graphql file:

type Mutation {
  //...
  resetTodos(todoIds: [ID!]!): BatchPayload!
}

If you wanted to reset ALL the todos you wouldn’t even need the parameter.

Then in our const resolvers – Mutation – resetTodos in our index.js, Something like below would reset ALL todos to true (Yes we’re “resetting” to true because I’m lazy):

resetTodos: (parent, args, context, info) => {
  return context.prisma.todo.updateMany({
    where: {
      isCompleted: false,
    },
    data: { isCompleted: true },
  });
},

But most likely you’ll want to only update records that meet multiple specific conditions, like only todos owned by a certain user. You can do that with the AND clause. So instead of using the resetTodos code above, set the resetTodos function the below code:

resetTodos: (parent, args, context, info) => {
  let newIds = args.todoIds.map((id) => {
    return parseInt(id);
  });
  return context.prisma.todo.updateMany({
    where: {
      AND: [
        {
          id: {
            in: newIds,
          },
        },
        {
          isCompleted: false,
        },
      ],
    },
    data: { isCompleted: true },
  });
},

We’re using one object for each condition. So the id must exist in the newIds array, and isCompleted must be set to false. If both of those are true, then set the isCompleted value to true for that specific Todo. As of now we just manually pass in the ids we want to delete into the args array. You could use authentication to reset based on the current user (We’ll cover auth soon)

Let’s test out our reset mutation. Create a few todos, get their ids and run the following:

mutation {
  resetTodos(todoIds: [4, 6, 7]) {
    count
  }
}

Update Multiple records with different values (Reordering for example)

Doing an update for something like reordering a list of elements is quite a bit harder, at least to do nicely.

If possible, you’ll want to restructure your data to avoid these types of updates, but I want to demonstrate that you’re in control of how you interact with data in the resolvers.

To re-order, we need to know which todoID belongs with which position, so we’ll want an array of objects with the values of the todoId / newPosition, then we pass that array as an argument to our reorder todos function. We’ll create a new “input type” of OrderChange to define the shape of the object.

Add the following code to your schema.graphql code:

type Mutation {
  updateManyTodos(newPositions: [OrderChange]): BatchPayload
}

input OrderChange {
  newPosition: Int!
  todoId: ID!
}

Now we’ll want to setup an asynchronous function and insert it into our const resolvers object. We’ll cover async functions in later post, but essentially they don’t “block” the execution thread. Right above the “const resolvers” declaration, paste the following code in index.js:

async function updateManyTodos(parent, args, context, info) {
  for (let i = 0; i < args.newPositions.length; ++i) {
    const todo = await context.prisma.todo.update({
      where: {
        id: parseInt(args.newPositions[i].todoId),
      },
      data: { order: args.newPositions[i].newPosition },
    });
  }
  return { count: args.newPositions.length }; // This is probably not good in case of errors.
}

Then inside the “Mutation”, in the “const resolvers”, we can place the updateManyTodos function to pass into GraphQLServer along with the others:

const resolvers = {
//...
  Mutation: {
    updateManyTodos,
//...

Restart the server, make sure you have some todos to reorder, and give it a shot with a GraphQL query similar to the following:

mutation {
  updateManyTodos(newPositions: [{todoId: 20, newPosition: 1}, {todoId: 21, newPosition: 2},
    {todoId: 22, newPosition: 3}, {todoId: 23, newPosition: 4}]) {
    count
  }
}

When you run this code you’ll get a “count” object in return. Now go check to make sure the todos have been reordered.

This solution is far from perfect. We’re returning the length of the array regardless of what happens in the resolvers. Probably shouldn’t return a “count” object because It’s not actually doing an “updateMany”, it’s just running a single update multiple times. This also does not scale well for large data sets. But since this is just a toy app, and use cases can vary wildly, I’m satisfied with this for now.

The final code for this section can be found here in the done-update-deletemany branch

That’s all for mutations for now. Let’s move on to the next thing.

Want More Tutorials?

Get our best crash courses and tutorials

(Source Code Provided)

Leave a Comment

Your email address will not be published. Required fields are marked *

want more details?

Fill in your details and we'll be in touch

%d bloggers like this: