Update Tasks with Mutations

Photo by NASA on Unsplash

Update Tasks with Mutations

·

3 min read

The last time was search by id form the task list. This time, I am going to update the list. It's called Mutation, which is a cool name in the GraphQL community :D

Code

const { ApolloServer, gql } = require('apollo-server');

const typeDefs = gql`
  type Task {
    id: ID!
    name: String!
    isActive: Boolean!
    createdAt: Int
    updatedAt: Int
    owner: String
  }

  type Query {
    tasks: [Task]
    task(id: ID!): Task
  }

  # ❶
  type Mutation { 
    addTask(name: String!): [Task]
    completeTask(id: ID!): [Task]
    deleteTask(id: ID!): [Task]
  }
`;

const tasks = [
  { id: 1, name: "Soak in an Onsen", isActive: true },
  { id: 2, name: "Sing Karaoke", isActive: false },
  { id: 3, name: "See cherry blossom", isActive: true },
]

// ❷
const newId = () => { return Math.max(...tasks.map((t) => t.id)) + 1 }

const resolvers = {
  Query: {
    tasks: () => tasks,
    task (parent, args, context, info) {
      const { id } = args;
      return context.db.find((task) => task.id == id)
    }
  },
  // ❸
  Mutation: {
    addTask: async (parent, args, context) => {
      context.db.push({
        id: newId(),
        ...args,
        isActive: true,
        createdAt: Math.floor(Date.now()/1000),
        updatedAt: Math.floor(Date.now()/1000)
      }) 
      return context.db
    },
    completeTask: async (parent, args, context) => {
      const targetTask = context.db.find(t => t.id == args.id)

      if (!targetTask) {
        throw new Error("No such task")
      } else {
        const idx = context.db.indexOf(targetTask)
        context.db[idx].isActive = !context.db[idx].isActive; 
        context.db[idx].updatedAt = Math.floor(Date.now()/1000); 
        return context.db
      }

    },
    deleteTask: async (parent, task, context) => {
      const targetTask = context.db.find(t => t.id == task.id)

      if (!targetTask) {
        throw new Error("No such task")
      } else {
        const idx = context.db.indexOf(targetTask)
        context.db.splice(idx, 1); 
        return context.db
      }

    }
  }
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: { db: tasks } 
});

server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`);
});

Updates from last time is as follows:

  1. Define the Types of the Mutationの型. I made all types as [Task] so that all of them retuns array of tasks.
  2. Function which generates an ID - not a good idea to scan the whole task array ids but it shold be OK for this kinda quick PoC.
  3. Resolver function which corresponds to the Type of ❶

That's it. I think I'm starting to understand the cycle of adding features. Add Type frist, then add Resolver as either inQuery or Mutaion section which corresponding to the Type

I repeated that cycle for addTask(), completeTask(), deleteTask()

Use the mutations

Now you can do a full range of CRUD processing. Let's make request from your client. The sample requests are:

# Write your query or mutation here

mutation add {
  addTask(name: "Buy some milk") {
    id
    name
  }
}

mutation delete {
  deleteTask(id: 5) {
    id
    name
  }
}

query list {
  tasks {
    id
    name
    isActive
    createdAt
    updatedAt
    owner
  }
}

mutation done {
  completeTask(id: 4) {
    id
    name
    isActive
  }
}

You should be able to resolve these queries with you GraphQL Playground at https://localhost:4000. Well, that was easier than I thought it would be :D

Next step

Now that we have a complete set of CRUD functions as TODO, next time I would like to modify the this Svelte frontend (REST version) that I wrote before and connect it to the GraphQL backend.