SDL 是建立 GraphQL Schema 的語言,而 Schema 則是定義 GraphQL API 的重要骨幹,包含資料架構格式和型別。
在了解 Schema 之前,先來了解 GraphQL 是什麼。
GraphQL
GraphQL 是一個用來查詢 API 的語言 (Query Language),也是一個基於型別系統來執行及查詢的 server-side runtime。
GraphQL 是設計用來快速開發高彈性、對開發者友善的 API。在開發完 API 後,會自動建立出一個線上的 IDE,在這個 IDE 中,可以測試發送 request,並得到相關的 response。在這個 IDE 中,也可以看得到完整的 API 文件。這邊有 Github 提供的 範例 IDE,有興趣的可以玩玩看~
在上方的 IDE 中,可以看到版面被切分成三個區塊。左邊是供 client 端撰寫的 query。Query 區塊下方可以寫入 query 要帶上的變數,並設定 request header。若要執行 query,可以按上方的播放鍵執行,執行後得到的結果就會出現在中間的區塊。而最右邊的就是自動建立出來的 API 文件。
當 GraphQL 的服務架起來時,便可以接受 GraphQL query,開始進行驗證及執行相關操作。GraphQL 會先確認 query 的型別與內容是否符合 Schema,驗證成功後才會執行 API 的資料存取。除此之外,GraphQL 還可以串接 RESTful API,詳細可以參考 官方教學 - Wrapping a REST API in GraphQL。
GraphQL 不依賴於任何特定的資料庫或是 storage engine,而是建立在開發者所撰寫出來的 code 及數據。透過 GraphQL 開發 API 時,首先要了解的就是他的 type System,以及該如何自定義型別 (define type),並依據每個 type 實作相對應的 resolver(可以理解成實作 API 的地方)。
所以接下來,就來看看建立 GraphQL 的 Type System 的 SDL。
存取方式
在 GraphQL 中,資料存取方式分成三種,分別是 query, mutation 跟 subscription。
- query:查詢資料
- mutation:新增、編輯、刪除資料
- subscription:擺脫以往發送 request 後得到 response 的方式,而是以透過 websocket 的方式,client 端訂閱某個資料後,Server 端一有新的資料後就會主動發送給 client。
資料型別 Scalar Types
- 可以讓 Client 與 Server 的開發者對於資料格式有共同的認知
- 強迫 Client 送出正確格式的 query
- 強迫 Server 回覆正確格式的 response
預設 Scalar Types
資料型別預設分成五種,分別是:
- Int:32-bit 整數
- Float:double precision 雙精度的浮點數
- String
- Boolean
- ID:ID 一般來說會以 string 方式呈現,但當前端輸入 id 為 “483109245” (string) 或是 483109245 (int) 都會被 GraphQL 所接受。
自訂 Scalar Types
除了以上的型別外,也可以自訂型別,常見的有 Date, URL, Email, JSON 等等。可以動手 實作,也可以透過 套件。
Syntax 語法
在了解以上基礎後,接下來就可以直接來看看,如何透過 SDL 訂定 GraphQL 的 Schema。
Comment
SDL 的註解方式分成三種,分別是 #
、"
及 """
。
#
:SDL 單行註解方式,不會呈現在自動生成的 GraphQL 文件中。
1 | # 這個單行註解不會出現在文件中 |
"
:SDL 單行註解方式,會呈現在自動生成的 GraphQL 文件中,經常用來備註 field definition 。
1 | " 這個單行註解會出現在文件中 " |
"""
:SDL 多行註解方式,會呈現在自動生成的 GraphQL 文件中。
1 | """ |
non-nullable !
!
在 SDL 當中,代表一個不能為 null
的值。GraphQL 會保證傳來的資料,不會是 null
。
String!
:這個值不可為null
,而且 scalar type 是字串[User!]
:這個值可能為null
,但陣列中不可為null
,且陣列中的 object type 為 User[User]!
:這個值不可能為null
,但陣列中可為null
,且陣列中的 object type 為 User[User!]!
:這個值不可為null
,且陣列中也不可為null
,陣列中的 object type 為 User
值得注意的是,假設今天設定資料為 myField: [String!]
,那可以猜猜看,以下哪個是合法的資料,哪個是不合法的:
1 | # 如果資料要求為 myField: [String!] |
為了不爆雷,答案在最下方,可以先想一下有答案後,再往下滑。
directive 指令
- directive 指令以
@
宣告 - 是一種語法糖
- 可以 custom directive
- 原生有三個指令,一個用在 schema 就是
@deprecated(reason: String!)
,另外兩個指令可參照 如何透過 GraphQL 存取資料 - Query。@deprecated(reason: String!)
:schema 使用,是用來呈現在文件上,告訴 client 端盡量不要存取該欄位的用法,因此一定需要帶上 reason 的值。
1 | type User { |
Schema
在還不太清楚前,可以先簡略的將 GraphQL Schema 想像成 DB Schema。一個 DB 可以有多個 Table,而一個 Schema 可以有多個 type;DB 中的 table 中有多個欄位,可分別設置資料型別,而 GraphQL type 也可以有多個 field (欄位),每個欄位的資料也可設定型別。
GraphQL 中的語法關鍵字 Schema
被官方稱為 Root Types,可以理解成所有存取資料的 entry point 進入點。在上面我們有提到存取方式有三種,因此 Schema
最多也只會包含三種類型:
1 | schema { |
Type / Field
type
是宣告Object Type
的關鍵字。Object Type
中若有可選的欄位,則是Field
。
底下範例中:
- Query 是 Object Type 的名字,供查詢。
- 設定可以查詢的 Field 欄位被稱為 Field Names,這邊有有四個,分別是 hello, me, users, user。
string
User
User!
[User!]!
被稱作 Field Types。users
這個 Field Name 需要帶上 argument 參數 name,詳見以下 argument。
1 | type Query { |
interface
透過
interface
關鍵字宣告介面。
在以下範例中 Character 是這個介面名稱,實作此介面的時候都必須包含以下三個欄位。(個人認為有點類似 OOP 的多型)。
1 | interface Character { |
implements
當要透過
type
實作出interface
時,都需要透過implements
這個關鍵字。
1 | """ |
enum
受限的選項,常用在參數、Field Types 等。
在以下的範例中,型別為 WeightUnit
的,value 只有可能有三種可能,分別是 KILOGRAM
、GRAM
、POUND
等。
1 | """ |
union
可以理解成 type 的集合,當今天回傳資料類型,可能包含一個以上的 type 時,union
就非常適合。
以下範例來說,會依照參數需求,來回傳需要的設備是使用手機還是電腦,這時候就很適合使用 union 回傳。
1 | union Device = Mobile | PC |
argument / input
參數,經常使用在 query 及 mutation 中。
input 是宣告 input 這個 Object Type 的關鍵字。
參數數量若大於 3 個的話,通常會包成一個 input。
1 | type Mutation { |
Answer
1 | # 如果資料要求為 myField: [String!] |
評論