iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 22
0
Modern Web

React + GraphQL 全端練習筆記系列 第 22

仿Trello - GraphQL Resolver簡介

  • 分享至 

  • xImage
  •  

本系列文以製作專案為主軸,紀錄小弟學習React以及GrahQL的過程。主要是記下重點步驟以及我覺得需要記憶的部分,有覺得不明確的地方還請留言多多指教。

之前建立Server時只建立了最簡單的resolver,現在來增加多一點功能,順帶簡介resolver撰寫的要點。

Query Resolver

首先再看一次要提供給resolver處理回傳的假資料:

const lists = [
  {
    id: 1,
    title: "list1",
  },
  {
    id: 2,
    title: "list2",
  },
];

const todos = [
  { id: 1, name: "todo1", listId: 1 },
  { id: 2, name: "todo2", listId: 1 },
  { id: 3, name: "todo3", listId: 2 },
  { id: 4, name: "todo4", listId: 2 },
  { id: 5, name: "todo5", listId: 2 },
];

在建立Apollo Server章節時有建立最基礎的Resolver範例,收到query lists指令後,回傳lists陣列資料。

const resolvers = {
  Query: {
    lists: () => lists,
  },
};

雖然lists陣列中的各list資料包含所有List定義的scalar欄位(id,title),但Server會根據graphQL指令只回傳需求的欄位,不再需要自行編寫過濾多餘欄位的程式碼,只要將整包資料交給Server就好。

像是這個 graphQL 指令:

query {
  lists {
    id
  }
}

收到的Data:

{
  "data": {
    "lists": [
      {
        "id": "1"
      },
      {
        "id": "2"
      }
    ]
  }
}

Resolver chains

由於lists陣列中不包含todos陣列資訊,所以光靠上面的resolver,是無法取得todos資訊的。

需要指明當Server收到 query lists todos時要如何對應,所以resolvers中需要定義 List todos欄位的對應方法:

const resolvers = {
  Query: {
    lists: () => lists,
  },
  List: {
    todos(parent, args, context, info) {
      return todos.filter((todo) => todo.listId === parent.id);
    },
  },
}

定義對應List todos欄位的查詢,要從todos陣列中撈出listId與 list.id相同的資料。

這裡List.todos resolver帶入了四個預設參數: parent, args, context, info,稍後會完整介紹一遍。先知道這裡只用上了parent參數,並且這個parent代表的是呼叫 todos的 List物件。

試試query效果:

query {
  lists {
    title
    todos{
      name
    }
  }
}

API回傳:

{
  "data": {
    "lists": [
      {
        "title": "list1",
        "todos": [
          {
            "name": "todo1"
          },
          {
            "name": "todo2"
          }
        ]
      },
      {
        "title": "list2",
        "todos": [
          {
            "name": "todo3"
          },
          {
            "name": "todo4"
          },
          {
            "name": "todo5"
          }
        ]
      }
    ]
  }
}

整個 graphQL指令的處理方式會像這樣:

這個過程被稱為Resolver Chain,理論上這條鍊可以一直接下去,只要欄位有對應的resolver。

為加強示範這個效果,將Todo形別加上參照的List欄位:

  type Todo {
    id: ID
    name: String!
    list: List #新增欄位
  }

然後給他對應的resolver:

const resolvers = {
  //...
  Todo: {
    list(parent) {
      return lists.find((list) => list.id == parent.listId);
    },
  },
}

最後在 play ground試試這個指令

query {
  lists {
    todos{
      list{
        todos{
          name
        }
      }
    }
  }
}

最後的結果太長這邊就不貼了。

小重點:
Resolver Chain最末端的Object型別(像上面的todos{name}),必須查詢至少一個scalar形別欄位。
不然你也不會需要這個Object型別。
若Resolver Chain有分支的話,就是各分支的末端要至少一個scalar形別。

Arguments

在進行Query或Mutations時可以帶入參數,像是

type Query {
    lists: [List]
    todo(id: ID!): Todo   //query todo時帶入id參數
}

對應的resolver可以從args參數中取出這些輸入參數,像是todo(id:ID!)對應的resolver:

todo: (parent, args) => {
  return todos.find((todo) => todo.id == args.id);
},

可以從args.id取得客戶端輸入的id參數。

實際的指令會像這樣:

query{
  todo(id:1){
    id
  }
}

如果制定了Input Type,像是:

 type Mutation {
    addTodo(params: addTodoParams): Todo
  }

  input addTodoParams {
    id: ID!
    name: String
  }

Resolver會變成從Input type的物件中提取參數:

addTodo(parent, args) {
  const { id, name } = args.params; //從args.params提取參數
  const newTodo = { id, name };
  return newTodo;
},

對應的請求指令:

mutation{
  addTodo(params: {id:7 ,name:"todo7"}){
    id 
    name
  }
}

Resolver 參數簡介:

一個Resolver預設依序有四個參數:

  1. parent :
    在resolver chain上前一個resolver取得的資訊,以這個chain為例

    List.todos()裡的parent就是Query.list()的回傳值,所以可以取得 list的id。

  2. arg:
    query或 mutation請求時輸入的參數,從上面的範例介紹應該很清楚了。

  3. context:
    所有resolver都能存取到同一個context,可以建立context存放像是身分驗證情報,或是資料庫連結等共用的資訊。

  4. info:
    包含許多關於目前執行的GraphQL請求的情報,像是經由的resolver chain path,具體怎麼利用還不清楚。

References:


上一篇
仿Trello - GraphQL Schema撰寫
下一篇
Prisma 簡介
系列文
React + GraphQL 全端練習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言