GraphQL

前幾天在 Vapor 的 slack 上看到這則訊息:

於是乎,便點了進去看看什麼是 GraphQL

GraphQL v.s REST

首先,看到這個標題,要先了解一下什麼是「REST」;

我們在先前的文章中有提到關於 Firebase Database REST API 的內容,

但並沒有在文章內解釋其含義是什麼。

REST

REST(Representational State Transfer),是一種架構,而非協定或是標準;

透過 HTTP 協議所提供的動作(POSTGETPUTDELETE)來對資源進行 CRUD(Create、Read、Update、Delete),

並以 URI 的方式來指定資源:

  • POST /games:建立遊戲
  • GET /games:取得遊戲清單
  • PUT /games/123:修改 id = 123 的遊戲內容
  • DELETE /games/123:刪除 id = 123 的遊戲

在 iOS 開發的串接 RESTful API 的經驗中,不論是取得的資料,或是回傳回 server 的資料,

大多是以 JSON 的格式在做溝通。

前後端之間可以透過 RESTful 的方式進行明確的溝通,進而開發出相對應的功能。

 

REST 的缺點

但有時候還是會遇到一些問題

  • 會拿到多餘的欄位
  • 巢狀資源的處理
  • 越來越多支 API

會拿到多餘的欄位

有時候我們為了顯示一個商品,在畫面上需要呈現

  • 商品名稱
  • 價錢
  • 照片 URL

但可能在 GET /products/1 的時候,會拿到整個商品的資料回來:

  • 商品 ID
  • 名稱
  • 價錢
  • 剩餘數量
  • 照片 URL
  • 建立時間
  • 最後更新時間

不過在顯示端僅需要呈現三個項目,那麼其他回傳的資料就浪費了彼此之間的網路傳輸。

巢狀資源的處理

像是在一個畫面中,需要顯示「我的最愛」;

在 REST 的架構下,我們會設計出兩支 API:

  • GET /users/1/favorites:取得 id = 1 的會員的最愛清單
  • GET /books/123:取得 id = 123 的書本資料

我們會在第一支 API 中取得一個書本 ID 列表,再來呼叫第二支 API 來取得書本資訊;

如果將書本的資訊直接併入最愛之中,雖然可以減少查詢的次數,但又會在某些不需要關聯的時候,

而讓書本成為多餘的欄位。

於是乎就會越來越多支 API

上述幾種狀況,可能就會開始出現:

  • GET /products-with-photo
  • GET /products-with-price
  • GET /products-with-users

之類的,為了配合某些前端的顯示需求而特化的 API,為了一次取得多個物件回傳,

但這也破壞了 REST 原先的架構設計風格。

所以,Facebook 便在 2012 年開始在公司內部使用另一種架設風格來解決這些問題:

GraphQL

GraphQL 是由 Facebook 所提出的一種 query language,從 2012 年內部開始使用,到 2015 年 7 月開源

其精神便是將所有的 API 都統整成一支 GraphQL 的 API:

  • GET /graphql?query={ user(id: “1″) { name, address { zipCode } } }

回傳的資料由呼叫 API 的人決定,有點像是讓呼叫人的負責下 query 的條件。

讓前後端共同使用一份 schema 來定義出個物件的 type 和定義好 Query 這個物件的內容,

再來就由呼叫方來自由發揮需要取得哪些資訊自訂組裝。

比較強烈的比較在於:

  • 只有一支 API(也就是只有一個 endpoint)
  • 可以自行處理巢狀資料
  • 可以自行決定拿到哪些欄位

進而改善上面所提到的問題。

 

結論

對於 iOS 的開發人員而言,是一種蠻新潮且有趣的串接方式,

但在後端就不知道是一個怎樣的複雜程度了⋯⋯畢竟我比較著重在 iOS 的應用方面,

有興趣的人可以看看我所一開始看的這篇文章

Firebase Database REST API

這篇主要的內容會是簡單地記錄一下 Firebase Database RESTful API

所提供的相關內容和使用方式。

Firebase Database

Firebase database 的儲存資料方式是屬於 NoSQL 的方式,

利用一組 key 配對一組 value 的模式來建構資料庫;

而在 Database 的介面中,我們可以清楚地看到資料是以 JSON 的格式呈現。

在 iOS 開發的過程中,如果要使用 Firebase 的相關內容,

可以使用官方所提供的 Firebase iOS SDK

或是在使用 Python 開發的時候,我會選擇使用官方推薦的 Pyrebase

那若你目前的開發方式沒有相對應的 SDK 或是 third party 可以使用的話呢?

那麼你就只能一起用 REST API 來完成要做的事情了!

REST API

Firebase 提供了五種 Http method

  • GET
  • PUT
  • POST
  • PATCH
  • DELETE

其中 GET 和 DELETE 就沒什麼特別好說的,你就是取得一個 JSON 或是刪除一個。

PUT

PUT 就和平常使用 PUT 的方式一樣,

它會把整個 JSON 覆蓋成你目前丟上去的 JSON。

POST

POST 的話就有些不一樣,當你 POST 一個資料到某個 JSON 的時候,

它會自行建立一組 key 並回傳 name 回來。

而你所丟的資料會是那組 key 所對應的 value,

所以簡單來說 POST 就是在做新增物件時使用,且 ID 是由它所建立的。

PATCH

PATCH 則是負責更新內容,它會先找到匹配的 key,再更新其 value;

若沒有找到相對應的 key 的話,則會建立一筆新的 key-value。

如果有其中一個無法使用呢?

若遇到你所使用的開發語言或是瀏覽器等等,無法使用其中一項 method 時;

舉個例子,DELETE 無法使用的話,Firebase 有提供你覆寫 method 的功能,

method 使用 POST,而在 header 加上:

X-HTTP-Method-Override: DELETE

便可以等同於上述所說的 DELETE。

或是加在 url 裡頭也可以:

https://%5BPROJECT_ID%5D.firebaseio/%5BJSON_NAME%5D.json?x-http-method-override=DELETE

 

Firebase ETag

Firebase 也有支援 ETag,

在 header 上加上 X-Firebase-ETag: true,它便會在回傳的 headers 中加上

Access-Control-Expose-Headers = ETag

ETag = kmHkuKx9sCx742tosJOV4oH+JBQ=

而我們可以下一次的 request 中,在 header 放上 if-match:[ETAG_VALUE]

伺服器端便會驗證是否可以執行這次的要求;

若最後一次的 ETag 和 if-match 的值相符的話,Firebase 便會回傳 412 Precondition failed。

而 ETag 相關的資訊可以看這邊

 

最後

整理完這些資訊後,就可以著手寫一些 database 的存取方法到 Vapor 的專案了!