🔨

俺でもわかるGraphQLでデータ更新(3)

2021/05/08に公開

はじめに

前回はIDによる検索でした。今回はデータ更新系をやってみます。GraphQL界ではMutationというかっこいい名前で呼ばれてるやつです[1]

コード

index.js
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}`);
});

変更点は以下

  1. Mutationの型(Type)を定義。いずれも[Task]としてタスクの配列を返すようにしておきました
  2. IDを自動で採番する関数作成
  3. ❶のTypeに対応するResolverの関数を書く

こんな感じで機能を足していけばいいみたいです。直感的にわかりやすいですね。

クエリ

これでひととおりCRUDできるようになりました。

# 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
  }
}

次回

これでTODOとしてはひととおりのCRUD機能が揃いましたので、次回は以前に書いた以下のSvelteのフロントエンド(REST版)を変更して、今回のGraphQLバックエンドに繋げてみたいと思います。

https://zenn.dev/masaino/articles/080f998d73351f

シリーズ

脚注
  1. かっこいいけど、わかりやすい呼び名ではないですね。Create, Update, Deleteの総称としていい名前が思いつかなかったのでしょうか ↩︎

Discussion