iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 21
0

前言

上一篇講到的 Query 是 CRUD 裡面的 R (讀取),這篇要講的 Mutation 則是負責處理 C (建立)、U (更新)、D (刪除) 等等的其他操作。延續上一篇的範例,這邊會繼續完成實作新增使用者更改使用者名稱刪除使用者三個 Mutation。

Mutation 簡介

雖然 GraphQL 的強項是 Query,但一個完整的系統還是要提供其他操作的能力,因此有了 Mutation。Mutation 看起來跟 Query 差不多,有欄位名稱、參數、回傳 Type:

type Mutation {
  mutationName(arg: Type!): ResponseType
}

雖然應該用 Query 來完成沒有副作用的查詢、用 Mutation 來執行其他有副作用的操作,但 GraphQL 跟 REST 一樣無法限制背後的實作。

不過 Query 跟 Mutation 還是有一些實際上的差別,官網上面有說明,當有許多欄位時 Query 會平行的去跑,而 Mutation 則會一個一個執行,這也可以在原始碼中看得到。

官網原文為:While query fields are executed in parallel, mutation fields run in series, one after the other.

接著就直接進入實際的實作來一窺究竟吧。

新增使用者

首先,先來複習一下上一篇所寫完的一些部分,usersById 是用來放置使用者資料的 Map:

const usersById = {
  1: {
    id: 1,
    name: 'chentsulin',
  },
};

GraphQLUser 則是用來 Resolve User 資料的 Class:

class GraphQLUser {
  constructor({ id, name }) {
    this.id = id;
    this.name = name;
  }

  // ...
}

複習完之後,我們先來加 addUser 的 Mutation,添加使用者的時候接一個 Stringname 參數,並回傳建好的 User

type Mutation {
  addUser(name: String!): User
}

接著要把 addUser 的 Resolving Function 也放到 rootValue 裡面,裡面包含的就是建立使用者的基礎邏輯:

let nextId = 2

const mutations = {
  addUser: ({ name }) => {
    const newUser = {
      id: nextId,
      name,
    };
    usersById[nextId] = newUser;
    
    nextId++;
    
    return new GraphQLUser(newUser);
  },
}

exports.rootValue = {
  ...queries,
  ...mutations,
};

要注意最下面這邊用到還在 Stage 3 的 Object Spread Properties Syntax,所以必須用 Babel 處理。不想麻煩的話可以利用 Object.assign

完成後執行下面的 Mutation 來看一下結果:

mutation {
  addUser(name: "Master") {
    id
    name
  }
}

再去查 users 確實能得到多一筆結果:

{
  users {
    id
    name
  }
}

更改使用者名稱

在 Mutation Type 上面加上 renameUser 這個 Mutation,接 IntidStringname,回傳修改完的 User

type Mutation {
  addUser(name: String!): User
  renameUser(id: Int!, name: String!): User
}

renameUser 的邏輯則更簡單明瞭,只是修改 name 的部分:

const mutation = {
  // ...

  renameUser: ({ id, name }) => {
    usersById[id].name = name;

    return new GraphQLUser(usersById[id]);
  },
};

完成後執行下面的 Mutation 來看一下結果:

mutation {
  renameUser(id: 1, name: "abcdefg") {
    id
    name
  }
}

刪除使用者

在 Mutation Type 上面加上 removeUser 這個 Mutation,接 Intid 然後回傳一個自定義的 Object Type RemoveUserPayload,上面包含被刪除的使用者 ID - deletedUserId

type RemoveUserPayload {
  deletedUserId: Int!
}

type Mutation {
  addUser(name: String!): User
  renameUser(id: Int!, name: String!): User
  removeUser(id: Int!): RemoveUserPayload
}

delete 來在 usersById 上面刪除對應的資料:

const mutation = {
  // ...

  removeUser: ({ id }) => {
    delete usersById[id];

    return {
      deletedUserId: id,
    };
  },
};

完成後執行下面的 Mutation 來看一下結果:

mutation {
  removeUser(id: 1) {
    deletedUserId
  }
}

結語

如果一開始就要做很難的東西自然會很難上手,不過我們從使用者的新增、修改、刪除、查詢的範例中簡單的看過了一下 GraphQL 的基礎,其他很多延伸的部分都環繞在程式語言跟後端本身的技術上,到這邊想必各位讀者已經可以自己去摸索更多的可能性了。


上一篇
Day 20:GraphQL 入門 Part III - Query
下一篇
Day 22:GraphQL 入門 Part V - Fragment
系列文
使用 Modern Web 技術來打造 Native App30

1 則留言

0
bxhred
iT邦新手 5 級 ‧ 2018-02-14 17:43:24

剛做完這篇 分享一下關於Object.assign 的寫法

    addUser: (name) => Object.assign({}, mutations.addUser(name)),
    renameUser: (id, name) => Object.assign({}, mutations.renameUser(id, name)),
    removeUser: (id) => Object.assign({}, mutations.removeUser(id))

我要留言

立即登入留言