---
tags: FrontEnd
---
# 什麼是 GraphQL

GraphQL 是一個 API 的查詢語言,他的主要目的在於能夠讓 client 以更為輕便彈性的方式去獲取資料。
## 所見即所得
GraphQL 的資料結構很易於理解,你可以把想要拿取的資料當作一張圖( Graph ),而你下的 query 則是幫助 GraphQL 去理解你要怎麼拿取資料。
假設我們想要拿取 user 的資料,就會下這樣的 query
```graphql=
query{
allUser{
id
firstName
age
}
}
```
然後我們就可以拿到這樣的資料
```json=
{
"data": {
"allUser": [
{
"id": "40",
"firstName": "Alex",
"age": 40
},
{
"id": "41",
"firstName": "Barney",
"age": 42
},
{
"id": "ej6TJGI",
"firstName": "喇叭詹",
"age": 23
},
//...
]
}
}
```
你不需要了解 server 做了什麼事或定義什麼,就可以預測從 query 拿取到的資料,因此才會說**所見即所得**。
## 精準拿取需要的資料
GraphQL 可以客製化你想要拿到的資料欄位,例如,我這次只想要拿到全部 user 的 firstname:
```graphql=
query{
allUser{
firstName
}
}
```
那我就只會收到含有 firstName 的一組資料
```json=
{
"data": {
"allUser": [
{
"firstName": "Alex"
},
{
"firstName": "Barney"
},
{
"firstName": "喇叭詹"
},
// ...
]
}
}
```
## 強型別
說到 GraphQL 的特點之一,就是他的**型別系統**,GraphQL 可以自定義物件,例如我們前面使用到的 user 物件:
```graphql=
type user{
id: ID!
firstName : String
age: Int
company: [Company]
}
type allUser{
user:[user]
}
```
而這些定義好的物件也就是 **schema**,可以說是 GraphQL 的基石之一,所有針對 GraphQL 的操作都是圍繞著 Schema 展開的。
順帶一提 GraphQL 本身就有以下幾種基本型別:
1. Int: 整數
1. Float: 浮點數
1. String: UTF‐8 字串
1. Boolean: True or False.
1. ID: 識別碼
而型別後面的 `!` 則是表示此欄位不可為空。
## 程式即文檔
得益於 graphQL 本身的型別系統,我們會拿到什麼資料,該送出什麼變數,都會在 schema 寫得清清處處,所以基本上不需要文件去額外描述該怎麼使用。
另外,graphQL 本身自帶 playground,只需要進入 `你的 host url /graphql`,就可以看到一旁的 document(你所定義的 schema)。

## 不綁定任和語言和資料庫
GraphQL [支援多種語言](https://graphql.org/code/),因此我們可以使用自己熟悉的語言來上手 GraphQL。

同時,GraphQL 擁有自由的實作方式,你可以使用任何方式獲取你的資料,而這時就不得不提到 GraphQL 的另一個基石 **resolver**( 以下使用在 js 的 graphql package實作 ):
```javascript=
const RootQuery = new GraphQLObjectType({
name:'RootQueryType',
fields:{
//...
user:{
type:UserType,
args:{id:{type:GraphQLString}},
resolve(parentValue,args){
// 這邊可以採用任何方式去拿取資料
// 這裡採用的是一個叫做 json-server 的假資料庫
return axios.get(`http://localhost:3000/users/${args.id}`).then(res=>{
return res.data
})
}
},
//...
}
})
```
resolver 定義了這個欄位該如何拿取資料,我們可以透過在 query 傳送參數,讓我們能夠拿取想要的資料:
```graphql=
# query
query{
user(id:"ej6TJGI"){
firstName
age
}
}
# 通常不會寫死 而是會這樣定義
# query($id:String){
# user(id:$id){
# firstName
# age
# }
# }
```
```json=
// data
{
"data": {
"user": {
"firstName": "喇叭詹",
"age": 23
}
}
}
```
## 巢狀資料結構
GraphQL 可以輕易拿取巢狀資料,讓我們不用透過多個 query 才能拿到所有完整資料:
```graphql=
# query
query($id:String){
user(id:$id){
firstName
age
company{
name
}
}
}
```
```json=
// data
{
"data": {
"user": {
"firstName": "喇叭詹",
"age": 23,
"company": {
"name": "Apple"
}
}
}
}
```
## 向後適應 lagacy API
GraphQL 只有一個 end point `/graphql`,而拿取資料的方式端看 schema 怎麼設定。
因此就算更新了 schema 的實作方式(例如更改 resolver 怎麼拿取資料),只要還是能夠符合之前的資料格式,就不需要去更改 client 的程式碼。
# vs REST?
雖說 GraphQL 是為了解決 RESTful API 的諸多問題才開發出來的,但GraphQL 還是有一些缺點,所以與其說是為了取代 RESTful API,不如說雙方是可以共用的。
以下將稍微提出 GraphQL 的部分缺點:
### 複雜的 query 方式
比起 RESTful 的毋須設定,只需要知道 endpoint 就可以使用,GraphQL 還是需要一些基礎設定才能在 client side 使用 `query` `mutation` 等功能。
### server side cache 困難
由於 GraphQL 不是使用 RESTful 的方式管理 request,因此每個 request 都是使用 POST 去實作,因此很難使用 web cacahe 的方式去快速拿取大量資料。
這部分我也不太懂,所以這邊放幾篇參考文章,有興趣的人可以深入看看:
https://hasura.io/graphql/caching/
https://www.digicentre.com.tw/industry_detail.php?id=44
https://www.apollographql.com/docs/apollo-server/performance/caching/
### 無法監控
RESTful 最大的好處之一就是遵循 HTTP 規範,因此只要單一 end point 有問題,就可以透過 statue code (5xx) 知道問題發生。
而 GraphQL 如果在 client 端發生問題的話,也只會回 200,所以對於錯誤捕捉可能需要花費額外的功夫,例如去監聽 client side 回傳回來的 message,但這個也需要 client 去額外處理。
### 無法控制 request
GraphQL 的好處就是可以讓 client side 也能夠進行 query,但這也導致了萬一有某一個請求可能會造成龐大的連鎖反應(例如 call schema a => schema b => schama c 如果每個都有幾千個,伺服器主機會直接可以煎蛋),可能會造成 server 很大負擔。
### 過於自由、規範少
由於 GraphQL 的實作方式過於自由,因此很難有一個best practice 可以參考,對於初次接觸的人來說,如果不太熟悉 schema 的定義方式,且用以往的 RESTful 方式思考的話,很容易埋下太多技術債。
### 學習曲線
雖說 GraphQL 不是一個太難的語言,但因為 client 和 server 都會需要碰觸,且 schema 也需要額外定義,用慣了 RESTful 可能會不習慣,在公司內推行也較為困難。
# 總結
這篇只是一篇推坑文,因此沒有寫得很細,而上面這些東西我實在是漏了很多細節沒說,因此有興趣的話還是要自己去官網看 document
例如:
1. 型別與定義: https://graphql.org/learn/schema/
1. 對資料的操作: https://graphql.org/learn/queries/
或是我有寫得詳細一點的版本,這篇像是整合版,但細節的話都在這兩篇會比較多。
1. [graphQL](https://superficial-trumpet-b43.notion.site/GraphQL-ba794770e34f4e4e841aafd51df630a1)
2. [why graphql](https://superficial-trumpet-b43.notion.site/why-GraphQL-b15d61ec64ba4f3d89fe5c9d49a22ec0)
另外,關於 GraphQL 的實作方式有很多種 package 可以做選擇,目前東西最多最完整的就是 [apollo](https://www.apollographql.com/docs/),可以看成是超值大禮包,裡面什麼東西都有。
關於本文的 demo code 在[這裡](https://github.com/oaoxd0314/pure-graphQL-server)。
# 參考
https://graphql.org/code/
https://www.apollographql.com/
[https://ithelp.ithome.com.tw/articles/10200678](https://ithelp.ithome.com.tw/articles/10200678) in frontEnd
[https://engineering.fb.com/2015/09/14/core-data/graphql-a-data-query-language/](https://engineering.fb.com/2015/09/14/core-data/graphql-a-data-query-language/) (meta engineering)
[https://www.appcoda.com.tw/graphql/](https://www.appcoda.com.tw/graphql/) in Swift(app)
[https://www.youtube.com/watch?v=ZQL7tL2S0oQ](https://www.youtube.com/watch?v=ZQL7tL2S0oQ)
https://www.moesif.com/blog/technical/graphql/REST-vs-GraphQL-APIs-the-good-the-bad-the-ugly/