在此頁面中,您將瞭解關於在類型系統的全部須要瞭解的內容,以及它如何描述能夠查詢哪些數據。因爲GraphQL能夠與任何後端框架或編程語言一塊兒使用,咱們將遠離具體的具體細節,只討論概念。編程
類型系統後端
若是您之前看到過一種查詢,您知道在查詢語言基本上是關於選擇對象上的字段。例如,在下面的查詢中:數組
{
hero {
name
appearsIn
}
}
結果緩存
{ "data": { "hero": { "name": "R2-D2", "appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI" ] } } }
因爲一種查詢的形狀與結果很是匹配,因此您能夠在不知道有關服務器的狀況的狀況下預測查詢返回的內容。可是,有一個咱們能夠要求的數據的精確描述是有用的-咱們能夠選擇哪些字段?他們會返回什麼樣的物體?在這些子對象上有哪些字段可用?這就是模式的用武之地。服務器
每一個GraphQL服務定義了一組類型,它們徹底描述了您能夠在該服務上查詢的可能數據集。而後,當查詢進入時,它們將被驗證並針對該模式執行。app
GraphQL服務能夠用任何語言編寫。因爲咱們不能依賴特定的編程語言語法(好比JavaScript)來討論GraphQL模式,咱們將定義本身的簡單語言。咱們將使用「GraphQL模式語言」-它相似於查詢語言,容許咱們以一種GraphQL的語言來討論基於語言的模式。框架
Graphql模式最基本的組件是對象類型,它們只表明您能夠從服務中獲取的對象類型,以及它擁有的字段。在GraphQL 模式語言中,咱們能夠這樣表示:編程語言
type Character { name: String! appearsIn: [Episode]! }
這門語言很容易讀,可是讓咱們來複習一下,這樣咱們就能夠有一個共同的詞彙:ide
l Character
是一個GraphQL對象類型,它包含了一些字段。模式中的大多數類型都是對象類型。函數
l name
和appearsIn
字段都屬於Character
類型。這意味着name
和appearsIn
是惟一能夠出如今運行於Character
的GraphQL查詢中的任何部分的字段。
l String
是內置標量類型之一-它們是解析爲單個scalar types的類型,而且在查詢中不能有子選擇。稍後咱們將更多地討論scalar types 。
l String! 表示字段是不可空的,這意味着在查詢此字段時,GraphQL服務承諾始終給出一個值。在類型語言中,咱們將用感嘆號表示那些。
l [Episode]! 表示一個Episode對象集合的數組。因爲它也是不能夠爲空的,因此當您查詢在字段時,您老是能夠獲得一個數組(包含零個或多個項)。
如今您知道了一種對象類型是什麼樣子的,以及如何讀取在類型語言的基本內容。
GraphQL對象中的每個字段均可以有一個或者多個參數,以length字段爲例:
type Starship { id: ID! name: String! length (unit: LengthUnit = METER): Float }
全部參數都被命名,與JavaScript和Python這樣的語言不一樣的是,函數中的全部參數都是按名稱傳遞的。在這個例子中,length字段定義了一個參數unit。
參數能夠是必需的,也能夠是可選的。當參數是可選的時,咱們能夠定義一個默認值-若是單元參數不傳遞,它將默認設置爲METER。
模式中的大多數類型都只是普通對象類型,但在模式中有兩種特殊類型:
schema {
query: Query
mutation: Mutation
}
每一個GraphQL服務都有一個query
類型,而且可能有或可能沒有mutation
類型。這些類型與常規對象類型相同,但它們是特殊的,由於它們定義了每一個GraphQL查詢的入口點。所以,若是您看到一個查詢,它看起來像:
query { hero { name } droid (id: "2000") { name } }
結果
{ "data": { "hero": { "name": "R2-D2" }, "droid": { "name": "C-3PO" } } }
這意味着在服務須要具備hero和droid字段的查詢類型:
type Query { hero (episode: Episode): Character droid (id: ID!): Droid }
Mutations 的工做方式相似-在Mutation
類型上定義字段,這些字段能夠做爲根mutation 字段,您能夠在查詢中調用這些字段。
重要的是要記住,除了做爲模式的「entry point" 的特殊狀態以外,Query
和Mutation
類型與任何其餘GraphQL對象類型同樣,它們的字段的工做方式徹底相同。
一種對象類型有一個名稱和字段,可是在某個時候這些字段必須解析爲一些具體的數據。這就是標量類型的來源:它們表明查詢的葉子。
在下面的查詢中,name和appearsIn將解析爲標量類型:
{
hero {
name
appearsIn
}
}
結果
{ "data": { "hero": { "name": "R2-D2", "appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI" ] } } }
咱們知道這一點,由於這些字段沒有子字段-它們是查詢的葉子。
GraphQL附帶了一組默認標量類型,以下:
l int:一個有符號的32位整數。
l 浮點:一個有符號的雙精度浮點值 。
l 字符串:一個UTF字符序列。
l 布爾:true或false。
l id:id標量類型表示惟一標識符,一般用於對對象進行從新定位或做爲緩存的鍵。id類型以與字符串相同的方式序列化。
在大多數GraphQL服務實現中,還有一種指定自定義標量類型的方法。例如,咱們能夠定義日期類型:
scalar Date
而後由咱們的實現來定義應該如何序列化該類型,反序列化並驗證。例如,能夠指定始終將日期類型序列化爲整數時間戳,而且你的客戶端知道指望要格式成日期字段。
枚舉類型是一種特殊的標量,它被限制在一組特定的容許值。這使您可以:
下面是一個定義在模式語言中的樣子:
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
這意味着不管咱們在咱們的模式中使用類型Episode
,咱們指望它徹底是一個NEWHOPE
,EMPIRE
,或者 JEDI。
對象類型、標量和枚舉是能夠在GraphQL中定義的惟一類型。可是,當您在模式的其餘部分使用類型時,或者在查詢變量聲明中,能夠應用影響這些值驗證的附加類型修飾符。
讓咱們看一個例子:
type Character { name: String! appearsIn: [Episode]! }
在這裏,咱們使用一個字符串類型並將它標記爲非空,添加一個感嘆號,!類型名稱以後。這意味着咱們的服務器老是指望返回這個字段的非空值,若是它最終獲得一個空值,實際上會觸發一個GraphQL執行錯誤,那麼讓客戶端知道出了問題。
在爲字段定義參數時,也可使用非空類型修飾符,這將致使在服務器返回一個驗證錯誤,若是一個空值做爲該參數傳遞,不管是在字符串仍是在變量中。
query DroidById($id: ID!) {
droid(id: $id) {
name
}
}
參數
{ "id": null }
結果
{ "errors": [ { "message": "Variable \"$id\" of required type \"ID!\" was not provided.", "locations": [ { "line": 1, "column": 17 } ] } ] }
列表的工做方式相似:咱們可使用類型修飾符將類型標記爲列表,指示此字段將返回該類型的數組。在模式語言中,這是經過將類型包裝在方括號中表示的,[和]。它對參數的做用是相同的,在這個參數中,驗證步驟將指望這個值的數組。
能夠將非空和列表修飾符組合在一塊兒。例如,您能夠有一個非空字符串列表:
myField: [String!]
這意味着列表自己能夠是null,但它不能有任何空成員。例如,在JSON:
myField: null // valid myField: [] // valid myField: ['a', 'b'] // valid myField: ['a', null, 'b'] // error
如今,假設咱們定義了一個非空的字符串列表:
myField: [String]!
這意味着列表自己不能爲空,但能夠包含空值:
myField: null // error myField: [] // valid myField: ['a', 'b'] // valid myField: ['a', null, 'b'] // valid
您能夠任意嵌套任意數量的非空和列表修飾符,根據您的須要。
與許多類型系統同樣,GraphQL支持接口。接口是一個抽象類型,它包含一個特定的字段集合,一個類型必須包含的實現接口的字段。
例如,你能夠有一個接口Character
,在星球大戰三部曲表明任何一個角色:
interface Character { id: ID! name: String! friends: [Character] appearsIn: [Episode]! }
這意味着任何實現Character的類型都須要這些精確的字段,其中包含這些參數和返回類型。
例如,下面是一些可能實現字符的類型:
type Human implements Character { id: ID! name: String! friends: [Character] appearsIn: [Episode]! starships: [Starship] totalCredits: Int } type Droid implements Character { id: ID! name: String! friends: [Character] appearsIn: [Episode]! primaryFunction: String }
您能夠看到,這兩個類型都具備Character接口中的全部字段,totalCredits
, starships
和 primaryFunction 這是區別於接口Character中的字段。
當您想要返回一個對象或一組對象時,接口是有用的,可是這些對象可能有幾種不一樣的類型。
例如,注意下面的查詢會產生一個錯誤:
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
primaryFunction
}
}
參數
{ "ep": "JEDI" }
結果
{ "errors": [ { "message": "Cannot query field \"primaryFunction\" on type \"Character\". Did you mean to use an inline fragment on \"Droid\"?", "locations": [ { "line": 4, "column": 5 } ] } ] }
在hero
字段返回類型Character,這意味着它多是一個Human
或一個Droid
,取決於episode
的參數。在上面的查詢中,您只能請求在Character接口上存在的字段,不包括primaryFunction。
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
}
}
參數
{ "ep": "JEDI" }
結果
{ "data": { "hero": { "name": "R2-D2", "primaryFunction": "Astromech" } } }
在查詢指南的行內片斷部分中瞭解有關此內容的更多信息。
聯和類型與接口很是類似,但它們不能指定類型之間的任何公共字段。
union SearchResult = Human | Droid | Starship
返回一個SearchResult類型,咱們均可以獲得一個Human,一個Droid,或者一個Starship。聯合類型的成員須要是具體的對象類型;您不能從接口或其餘聯合建立一個聯合類型。
在這種狀況下,若是查詢返回在SearchResult
聯合類型的字段,則須要使用條件片斷才能查詢任何字段:
{ search(text: "an") { ... on Human { name height } ... on Droid { name primaryFunction } ... on Starship { name length } } }
結果
{ "data": { "search": [ { "name": "Han Solo", "height": 1.8 }, { "name": "Leia Organa", "primaryFunction": "Astromech" }, { "name": "TIE Advanced x1", "length": 9.2 } ] } }
到目前爲止,咱們只討論了將標量值(如枚舉或字符串)做爲參數傳遞到字段中。可是,您也能夠輕鬆地傳遞複雜的對象。在發生mutations的狀況下,這一點特別有用,您可能但願傳入要建立的整個對象。
在 GraphQL schema 語言中。輸入類型與常規對象類型徹底相同,但使用input
而不是type:
input ReviewInput { stars: Int! commentary: String }
下面是如何在一種修改中使用input對象類型:
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
參數
{ "ep": "JEDI", "review": { "stars": 5, "commentary": "This is a great movie!" } }
結果
{ "data": { "createReview": { "stars": 5, "commentary": "This is a great movie!" } } }
輸入對象類型上的字段能夠本身引用輸入對象類型,但不能在模式中混合輸入和輸出類型。輸入對象類型也不能在其字段上有參數。