【譯】GraphQL 初學者指南

Seymoe發表於2019-01-06

API 是網際網路行業討論最多的術語之一,但是很多人並不確切知道 API 到底是什麼。基本上,API 代表應用程式程式設計介面(Application Programming Interface)。顧名思義,它相當於一個“介面”,使人們(開發人員、使用者、消費者)能夠通過它與資料進行互動。

你可以理解為 API 就像是個酒保,你向酒保要一杯酒,他便把一杯你想要的酒遞給你,看起來很簡單的事,還有什麼問題呢?

構建 API 不是一件困難的事,但學習和理解具體的 API 可能存在一定的困難。多數開發者都會使用你的 API 去開發功能或者只是消費資料,這就要求 API 需要儘可能保持乾淨、直觀。精心設計、直觀性強的 API 會非常容易理解和使用。

一直以來,我們都在用 REST 來構建 API,使用這種方式會存在一些問題,例如:

  • 多端點
  • 開發人員學習理解 API 的成本比較高
  • API 的資訊過度和資訊不足問題

為了解決以上的類似問題,Facebook 創造了 GraphQL。我認為 GraphQL 是當代構建 API 的最佳方式,本文將闡述為什麼你應該開始學習 GraphQL,讓你瞭解 GraphQL 的工作原理,以及如何使用 GraphQL 建立設計精良、高效並且功能強大的 API。

你可能已經聽說過 GraphQL,因為很多開發者和公司都在使用它。由於 GraphQL 是開源的,所以社群也是很龐大的存在。讓我們在實踐中學習 GraphQL 的工作原理,領略它的魔力了。

什麼是GraphQL?

GraphQL (譯者注:中文文件)是 Facebook 開發的一種開源查詢語言。它為我們提供了一種更有效的方式來設計、建立和消費我們的 API 。基本上,它是 REST 的替代品。

GraphQL 有很多功能,例如:

  1. 我們可以編寫自己所需的資料,並獲得所需的資料。不用再像我們習慣使用的 REST 那樣過度獲取資訊
  2. GraphQL 為我們提供了單個端點,不再為同一 API 提供版本2或版本3。
  3. GraphQL 是強型別的,所以我們可以在執行之前在 GraphQL 型別系統中驗證查詢。有助於構建更強大的 API。

這是對 GraphQL 的基本介紹,解釋了為什麼它如此強大以及為什麼它如今獲得了很多人氣。如果想了解更多相關資訊,我建議訪問 GraphQL 網站去了解。

入門

本文主要目的不是學習如何配置 GraphQL 伺服器,所以我們現在不會深入研究。本文的目標是瞭解 GraphQL 在實踐中的工作原理,因此我們將使用一個叫做☄️Traderpack的零配置GraphQL伺服器庫。

第一步,我們需要建立一個新資料夾,你可以隨意命名。我將它命名為 graphql-server :

mkdir graphql-server
複製程式碼

假定你已經在自己的機器中安裝了 npm 或 yarn。如果你不知道它們是什麼,npm 和 yarn 是 JavaScript 程式語言的包管理器。對於 Node.js,預設包管理器是 npm。

在建立的資料夾中,輸入以下命令:

npm init -y
複製程式碼

如果你使用 yarn :

yarn init 
複製程式碼

npm 會建立一個 package.json 檔案,專案安裝的所有依賴項和命令都在這個檔案中。

接下來,我們來安裝本專案要使用的唯一依賴項,☄️Traderpack 允許你建立零配置的 GraphQL 伺服器。由於我們剛剛開始使用 GraphQL,這將幫助我們繼續學習更多內容,而不必擔心伺服器配置。

開啟終端進入專案資料夾,鍵入以下命令:

npm install --save-dev graphpack
複製程式碼

如果你使用 yarn,應該這樣:

yarn add --dev graphpack
複製程式碼

安裝 Graphpack 之後,轉到 package.json 檔案中的指令碼,並在其中輸入以下程式碼:

"scripts": {
    "dev": "graphpack",
    "build": "graphpack build"
}
複製程式碼

我們將建立一個名為 src 的資料夾作為整個伺服器中唯一的資料夾,然後在 src 資料夾中建立三個檔案。

譯者注:使用 VSCode 的童鞋可以安裝 `GraphQL for VSCode` 外掛進行語法高亮

首先,我們建立第一個檔案,名叫 schema.graphql,並在這個檔案中輸入以下程式碼:

type Query {
  hello: String
}
複製程式碼

這個 schema.graphql 檔案將是我們的整個 GraphQL 架構,稍後我會解釋它的作用。

接下來,在 src 資料夾中建立第二個檔案 resolvers.js,並在這個檔案中輸入以下程式碼:

import { users } from "./db"

const resolvers = {
  Query: {
    hello: () => "Hello World!"
  }
}

export default resolvers
複製程式碼

這個 resolvers.js 檔案將是我們提供將 GraphQL 操作轉換為資料的指令的方式。

最後,在src資料夾中建立第三個檔案 db.js,並在這個檔案中輸入以下程式碼:

export let users = [
  { id: 1, name: "John Doe", email: "john@gmail.com", age: 22 },
  { id: 2, name: "Jane Doe", email: "jane@gmail.com", age: 23 }
]
複製程式碼

在本教程中,我們沒有使用真實資料庫。所以這個 db.js 檔案將模擬資料庫,僅用於學習目的。

現在我們的src資料夾應如下所示:

src
  |--db.js
  |--resolvers.js
  |--schema.graphql
複製程式碼

現在,如果你執行命令 npm run dev,或者如果你正在使用yarn,則執行 yarn dev,應該在終端中看到此輸出:

【譯】GraphQL 初學者指南

現在可以用瀏覽器開啟 localhost:4000,這意味著我們已準備好開始在 GraphQL 中編寫我們的第一個 queries(查詢),mutations(突變)和 subscriptions(訂閱)。

如果感興趣可以看看 GraphQL Playground,這是一個功能強大的 GraphQL IDE,可用於更好的開發工作流程。如果你想了解有關 GraphQL Playground 的更多資訊,請單擊此處

Schema

GraphQL 有自己的語言型別,用於編寫 Schema(模式)。這是一種人類可讀的模式語法,稱為 Schema Definition Language - 模式定義語言(SDL)。無論使用何種技術,SDL都是相同的 - 你可以將其用於你想要的任何語言或框架。這種模式語言非常有用,因為它會讓你很容易直觀的理解你的 API 將具有哪些型別。

Types

Types(型別)是 GraphQL 最重要的特性之一。Types 是自定義物件,表示 API 的外觀。舉個例子,如果你正在構建社交媒體應用程式,那麼你的 API 可能會有 PostsUsersLikesGroups 等 Types。

Types 下有 fields(欄位),這些欄位返回特定型別的資料。例如,我們要建立一個 User 型別,我們應該有 nameemailage 等欄位。欄位可以是任何型別,並始終返回一種資料型別,比如 Int、Float、String、Boolean、ID、物件型別列表或自定義物件型別。

現在編寫我們的第一個 Type,轉到 schema.graphql 檔案並用以下內容替換已存在的 Query 型別:

type User {
  id: ID!
  name: String!
  email: String!
  age: Int
}
複製程式碼

每個 User 都將擁有一個 ID,因此我們為其提供了 ID 型別。User 也會有一個 nameemail,所以我們給它們 String 型別,年齡我們給了 Int 型別。很簡單吧?

資料型別後面的 ! 表示該欄位非空(non-nullable),這意味著這些帶 ! 的欄位必須在每個查詢中返回一些資料。

在 GraphQL 中有三個主要概念:

  1. 查詢(queries) - 從伺服器獲取資料
  2. 突變(mutations) - 修改伺服器上的資料並獲取更新資料(建立,更新,刪除)
  3. 訂閱(subscriptions) - 與伺服器保持實時連線

這三個主要概念我會一一解釋。

Queries

簡單來說,GraphQL 中的查詢就是獲取資料的方式。GraphQL 中的查詢會獲得所需的確切資料。不多也不少。這對我們的 API 產生了巨大的積極影響 - 不再像我們使用 REST API 那樣過度獲取或提取不足資訊。

讓我們來中建立一個查詢型別,首先,在 schema.graphql中新增一個名為 Query 的新型別:

type Query {
  users: [User!]!
}
複製程式碼

users 查詢將返回給我們一個或多個使用者的陣列。它不會返回 null,因為我們在後面加了 ! ,這意味著它是一個不可為空的查詢,應該總是返回一些東西。

既然能返回多個,我們也能返回一個特定使用者,在 Query 型別中新增一個新的查詢 user:

  type Query {
    users: [User!]!
+   user(id: ID!): User!
  }
複製程式碼

你會發現查詢能夠傳遞引數,查詢特定使用者的時候把使用者的 id 作為引數傳入。

那麼 GraphQL 如何知道去哪獲取到資料返回呢?這就是 resolvers.js 的作用,它會告訴 GraphQL 如何以及在何處獲取資料。

開啟 resolvers.js 檔案,更改一下程式碼:

import { users } from "./db"

const resolvers = {
  Query: {
    user: (parent, { id }, context, info) => {
      return users.find(user => user.id === id)
    },
    users: (parent, args, context, info) => {
      return users
    }
  }
}

export default resolvers
複製程式碼

解釋一下以上程式碼是如何工作的:

  • 每個查詢解析器都有四個引數。在 user 函式中,我們將 id 作為引數傳遞,然後返回與傳遞的 id 匹配的特定使用者
  • users 函式中,我們只是返回已存在的 users 陣列,它會返回所有的使用者

現在,我們將測試我們的查詢是否正常工作。瀏覽器開啟 localhost:4000,在左邊輸入以下程式碼然後點執行按鈕:

query {
  users {
    id
    name
    email
    age
  }
}
複製程式碼

試試返回 id 為 1 的使用者:

query {
  user(id: 1) {
    id
    name
    email
    age
  }
}
複製程式碼

Mutations

在 GraphQL 中,Mutations 是修改伺服器上的資料並獲取更新資料的方式。你可以理解為類似 REST 的CUD(CREATE,UPDATE,DELETE)。

我們來建立一個型別突變,我們所有的突變都將在這種型別中結束。在 schema.graphql 檔案中編寫一個名為 mutation 的新型別:

type Mutation {
  createUser(id: ID!, name: String!, email: String!, age: Int): User!
  updateUser(id: ID!, name: String, email: String, age: Int): User!
  deleteUser(id: ID!): User!
}
複製程式碼

上述程式碼建立了三個 Mutation:

  1. createUser - 我們傳入的引數 idnameemailage,應該返回一個新的使用者
  2. updateUser - 傳入 id 和新的 nameemailage, 應該返回一個更新後的使用者
  3. deleteUser - 傳入 id ,應該返回被刪除掉使用者的資訊

現在,我們去 resolvers.js 檔案中,在 Query 物件下方插入一個新的 Mutation 物件。

Mutation: {
    createUser: (parent, { id, name, email, age }, context, info) => {
      const newUser = { id, name, email, age }
      users.push(newUser)
      return newUser
    },
    updateUser: (parent, { id, name, email, age }, context, info) => {
      let newUser = users.find(user => user.id === id)
      newUser.name = name
      newUser.email = email
      newUser.age = age
      return newUser
    },
    deleteUser: (parent, { id }, context, info) => {
      const userIndex = users.findIndex(user => user.id === id)
      if (userIndex === -1) throw new Error("User not found.")
      const deletedUsers = users.splice(userIndex, 1)
      return deletedUsers[0]
    }
  }
複製程式碼

該去 localhost:4000 看看我們寫的 Mutations 是否有效了,在頁面上輸入:

mutation {
  createUser(id: 3, name: "Robert", email: "robert@gmail.com", age: 21) {
    id
    name
    email
    age
  }
}
複製程式碼

你也可以嘗試一下其他的 Mutation。

Subscriptions

正如我之前所說,Subscriptions (訂閱)是你與伺服器保持實時連線的方式。這意味著無論何時在伺服器中發生事件,並且每當呼叫該事件時,伺服器都會將相應的資料傳送到客戶端。通過使用訂閱,你可以將應用程式更新為不同使用者之間的最新更改。

最基本的訂閱示例:

subscription {
  users {
    id
    name
    email
    age
  }
}
複製程式碼

你會說它與查詢非常相似,但它的工作方式和查詢不同。當伺服器中的某些內容更新時,伺服器將執行訂閱中指定的 GraphQL 查詢,並將更新的結果傳送到客戶端。我並不打算在這篇文章中使用訂閱,但如果你想了解更多關於它們的資訊,請點選此處

總結

如你所見,GraphQL 是一項非常強大的新技術。它為我們提供了構建更好和精心設計的 API 的真正能力。這就是為什麼我建議你現在開始學習它。

對我來說,它最終將取代 REST。

感謝閱讀文章,請在下面發表評論!

相關文章