Graphql入門

Graphql入門

GraphQL是一個查詢語言示惊,由Facebook開發(fā)赴涵,用于替換RESTful API。服務端可以用任何的語言實現(xiàn)鸽嫂。具體的你可以查看Facebook關于GraphQL的文檔各種語言的實現(xiàn)

GraphQL的小歷史

早在2012年纵装,F(xiàn)acebook認為人們只有在離開PC的時候才會用智能手機,很快他們就發(fā)現(xiàn)這個認識是多么的錯誤据某!于是Facebook把注意力從Web移到了智能終端上橡娄。在那個時候,他們嚴重的依賴于RESTful API癣籽。大量的并發(fā)請求和對補充數(shù)據(jù)的二次請求給他們造成了很大的麻煩挽唉,尤其是響應時間。一個解決方案是設計足夠多的資源來滿足單次的請求筷狼。但是瓶籽,這造成了服務端的擴展和維護困難。

在尋找更好的解決方案的過程中埂材,F(xiàn)acebook的工程師發(fā)現(xiàn)開發(fā)人員不應該先入為主的把數(shù)據(jù)看成RESTful一樣的集合塑顺。如何更好地存儲和獲取數(shù)據(jù)不應該是他們要主要考慮的內(nèi)容。他們應該更多的考慮數(shù)據(jù)的關系楞遏,網(wǎng)狀的關系茬暇。

在這個情況下GraphQL應運而生。

GraphQL工作機制

一個GraphQL查詢可以包含一個或者多個操作(operation)寡喝,類似于一個RESTful API糙俗。操作(operation)可以使兩種類型:查詢(Query)或者修改(mutation)。我們看一個例子:

query {
  client(id: 1) {
    id 
    name
  }
}

你的第一印象:“這個不是JSON预鬓?”巧骚。還真不是赊颠!就如我們之前說的,GraphQL設計的中心是為客戶端服務劈彪。GraphQL的設計者希望可以寫一個和期待的返回數(shù)據(jù)schema差不多的查詢竣蹦。

注意上面的例子有三個不同的部分組成:

  • client是查詢的operation
  • (id: 1)包含了傳入給Query的參數(shù)
  • 查詢包含idname字段,這些字段也是我們希望查詢可以返回的

我們看看server會給這個查詢返回什么:

{
  "data": {
    "client": {
      "id": "1",
      "name": "Uncle Charlie"
    }
  }
}

就如我們期望的沧奴,server會返回一個JSON串痘括。這個JSON的schema和查詢的基本一致。

我們再看看另一個例子:

query {
  products(product_category_id: 1, order: "price DESC") {
    name 
    shell_size
    manufacturer
    price
  }
}

這次我們查詢products滔吠,并傳入兩個參數(shù):product_category_id用于過濾纲菌,一個指明按照price字段降序排列。查詢中包含的字段是:name疮绷、shell_size翰舌、manufacturerprice)。

你可能已經(jīng)猜到返回的結果是什么樣子的了:

{
  "data": {
    "products": [
      {
        "name": "Mapex Black Panther Velvetone 5-pc Drum Shell Kit",
        "shell_size": "22\"x18\" Bass Drum, 10\"x8\" & 12\"x9\" Toms, 14\"x14\" & 16\"x16\" Floor Toms",
        "manufacturer": "Mapex",
        "price": 2949.09
      },
      {
        "name": "Pearl MCX Masters Natural Birdseye Maple 4pc Shell Pack with 22\" Kick",
        "shell_size": "22x18\" Virgin Bass Drum 10x8\" Rack Tom 12x9\" Rack Tom 16x16\" Floor Tom",
        "manufacturer": "Pearl",
        "price": 1768.33
      }
    ]
  }
}

從這幾個初級的例子里你可以看出來GraphQL允許客戶端明確指定它要的是什么冬骚,避免了數(shù)據(jù)后去的冗余或者不足椅贱。和RESTful API對比一下,每一個客戶端都會對應很多個RESTful API或者一個API要服務很多個客戶端只冻。所以說GraphQL是很好的查詢語言庇麦。所有的operation、參數(shù)和所有可以查詢的字段都需要在GraphQL server上定義属愤、實現(xiàn)女器。

GraphQL還解決了另外一個問題。假設我們要查詢product_categories和相關的products住诸。在一個RESTful server上你可以實現(xiàn)一個API,返回全部的數(shù)據(jù)涣澡。但是贱呐,大多數(shù)的情況下,客戶端會先請求product_categories之后在其他的請求中獲取相關的某些products入桂。

我們看看使用GraphQL可以怎么做:

query {
  product_categories {
    name 
    products {
      name 
      price
    }
  }
}

我們這一次沒有使用參數(shù)奄薇。在查詢中我們指定了我么需要每一個product_categoryname,還有所有的這個類別下的產(chǎn)品抗愁,每個產(chǎn)品的字段也都分別指定馁蒂。返回的結果:

{
  "data": {
    "product_categories": [
      {
        "name": "Acoustic Drums",
        "products": [
          {
            "name": "Mapex Black Panther Velvetone 5-pc Drum Shell Kit",
            "price": 2949.09
          },
          {
            "name": "Pearl MCX Masters Natural Birdseye Maple 4pc Shell Pack with 22\" Kick",
            "price": 1768.33
          }
        ]
      },
      {
        "name": "Cymbals",
        "products": [
          {
            "name": "Sabian 18\" HHX Evolution Crash Cymbal - Brilliant",
            "price": 319
          },
          {
            "name": "Zildjian 20\" K Custom Dry Light Ride Cymbal",
            "price": 396.99
          },
          {
            "name": "Zildjian 13\" K Custom Dark Hi Hat Cymbals",
            "price": 414.95
          }
        ]
      }
    ]
  }
}

查詢的嵌套沒有限制,全看我們的查詢和server的實現(xiàn)蜘腌。比如西面的例子完全合法:

{
  purchases(client_id: 1) {
    date
    quantity
    total
    product {
      name
      price
      product_category {
        name
      }
    }
    client {
      name
      dob
    }
  }
}

這里我們請求server返回某個客戶的purchases沫屡。查詢里不僅指定了purchase的字段,還指定了相關的product撮珠,product_category的名稱沮脖。

GraphQL有非常重要的一個特點:強類型

每一個GraphQL server都要定義類型系統(tǒng)。查詢實在這個類型系統(tǒng)的上下文中執(zhí)行的勺届。

也就是說驶俊,你可以查詢值類型:Int, Float, String, BooleanID

而上例中的purchase里的字段免姿,product饼酿、clientproduct_category都是對象類型(Object Type)的。這些類型都需要我們自己定義胚膊。

由于GraphQL查詢都是結構化的嗜湃,信息也是類樹結構展示的。值類型(Scalar Type)的可以理解為葉子澜掩,對象類型(Object Type)可以理解為樹干购披。

操作(Operation)和字段別名

在GraphQL查詢中可以為Operation里的字段指定別名。比如查詢里指定了字段cymbal_size肩榕,但是客戶端只能接受diameter刚陡。另外查詢的返回結果都包含在以operation名稱為key的對象里,所以這個名稱也可以設置一個別名:

{
  my_product: product(id: 3) {
    id 
    name
    diameter: cymbal_size
  }
}

返回的數(shù)據(jù):

{
  "data": {
    "my_product": {
      "id": "3",
      "name": "Zildjian 13\" K Custom Dark Hi Hat Cymbals",
      "diameter": "13\""
    }
  }
}

Fragments

現(xiàn)在株汉,客戶端APP要獲取另個分開的list: drum setscymbals筐乳。在GraphQL里你不會被限制在一個operation里。同時我們也可以像設置字段別名那樣設置返回結果的別名:

query {
  drumsets: product(product_category_id: 1) {
    id
    name
    manufacturer
    price
    pieces
    shell_size
    shell_type
  }

  cymbals: products(product_category_id: 2) {
    id
    name
    manufacturer
    price
    cymbal_size
  }
}

你可能已經(jīng)注意到乔妈,在查詢的兩個對象中都包含了字段:id蝙云、namemanufacturer路召、price勃刨。

為了避免重復字段,我們可以使用GraphQL提供的Fragments股淡。我們來把重復的字段都提出來身隐,放到一個fragment里:

query {
  drumsets: products(product_category_id: 1) {
    ...ProductCommonFields
    prices
    shell_size
    shell_type
  }

  cymbals: products(product_category_id: 2) {
    ...ProductCommonFields
    cymbal_size
  }
}

fragment ProductCommonFields on Product {
  id
  name
  manufacturer
  price
}

要使用一個Fragment就使用操作符:...

變量(Variable)

我們要減少查詢語句中的重復唯灵,我們來看看另外的一個例子該如何處理:

client(id: 1) {
  name
  dob
}

purchasses(client_id: 1) {
  date
  quantity
  total
  product {
    name 
    price
    product_category {
      name
    }
  }
  client {
    name 
    dob 
  }
}

我們使用兩個operation查詢server贾铝,并且每個都包含了client_id參數(shù)。如果可以把這個集中到一起就非常好了埠帕。我們可以使用GraphQL的變量來實現(xiàn)這個效果垢揩。我們來添加一個clientID變量。

query($clientId: Int) {
  client(id: $clientId) {
    name
    dob
  }

  purchases(client_id: $clientId) {
    date
    quantity
    total
    product {
      name
      price
      product_category {
        name
      }
    }
    client {
      name
      dob
    }
  }
}

我們在operation的前面定義了變量敛瓷,然后我們就可以在整個查詢中使用這個變量了叁巨。 為了使用變量的定義,我們需要在查詢的時候附帶變量值的JSON琐驴。

{
  "clientId": 1
}

當然俘种,我們也可以指定一個默認值:

query ($date: String = "2017/01/28") {
  purchases(date: $date) {
    date
    quantity
    total
  }
}

Mutation(修改)

GraphQL不僅可以用來查詢數(shù)據(jù)秤标,也可以創(chuàng)建、更新和銷毀數(shù)據(jù)宙刘。當然和查詢一樣苍姜,這些也需要server端有對應的實現(xiàn)。增悬包、刪衙猪、改一類的operation在GraphQL里統(tǒng)稱為Muration(修改)。我們就通過幾個例子來演示一下mutation布近。

mutation {
  create_client (
    name: "查理大叔"
    dob: "2017/01/28"
  ) {
    id 
    name
    dob
  }
}

我們現(xiàn)在指定operation的類型為mutation垫释,而不是query。在create_client操作里我們傳入了創(chuàng)建一個client需要的數(shù)據(jù)撑瞧,并最終返回一個查詢集合:

{
  "data": {
    "create_client": {
      "id": "5",
      "name": "查理大叔",
      "dob": "2017/01/28"
    }
  }
}

上面的數(shù)據(jù)有一點錯誤棵譬,生日不對。下面就來用更新來fix這個錯誤:

mutation {
  update_client (
    id: 5
    dob: "1990/01/01"
  ) {
    id
    name
    dob
  }
}

最后预伺,如果我們要刪除這個數(shù)據(jù)可以這樣:

mutation {
  destroy_client(id: 5) {
    name 
    dob
  }
}

注意:create_client订咸、update_clientdestroy_client這些operation都是在GraphQL server實現(xiàn)好的。如果有什么方法可以知道GraphQL server都實現(xiàn)了什么方法不是很好酬诀,是的有GraphQL的doc可以查看脏嚷。

定義說明

GraphQL的一個非常好的特性就是,它會根據(jù)已經(jīng)定義好的類型系統(tǒng)來自動生成出一個說明文檔瞒御。這樣你就不用一次一次的翻看代碼父叙,而直接查看文檔來了解operation的全部實現(xiàn)細節(jié)。如果你用的是express-graphql, 并設置graphiqltrue的話肴裙,那么就會生成一個web的調(diào)試界面趾唱。在最右側可以直接使用doc:

app.use('/mobile/egoods', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
  pretty: IS_DEVELOPMENT,
}))

或者,也可以使用對于應定義好的schema的查詢践宴,如:

{
  __schema {
    queryType {
      name 
      fields {
        name
      }
    }
  }
}

結果為:

{
  "data": {
    "__schema": {
      "queryType": {
        "name": "Query",
        "fields": [
          {
            "name": "client"
          },
          {
            "name": "clients"
          },
          {
            "name": "product"
          },
          {
            "name": "product_categories"
          },
          {
            "name": "product_category"
          },
          {
            "name": "products"
          }
        ]
      }
    }
  }
}

對于mutation類型的操作也是一樣的:

{
  __schema {
    mutationType {
      name
      fields {
        name
      }
    } 
  }
}

查詢的結果為:

{
  "data": {
    "__schema": {
      "mutationType": {
        "name": "Mutation",
        "fields": [
          {
            "name": "create_client"
          },
          {
            "name": "destroy_client"
          },
          {
            "name": "update_client"
          }
        ]
      }
    }
  }
}

就像上文展示的一樣鲸匿,你還可以查詢很多其他的內(nèi)容。比如:

{
  __schema {
    queryType {
      name
      fields {
        name
        args {
          name
        }
      }
    }
  }
}

我們來簡單的看看結果是什么樣的:

{
  "data": {
    "__schema": {
      "queryType": {
        "name": "Query",
        "fields": [
          {
            "name": "clients",
            "args": [
              {
                "name": "ids"
              },
              {
                "name": "name"
              },
              {
                "name": "dob"
              }
            ]
          },
          {
            ...
          },
          {
            "name": "products",
            "args": [
              {
                "name": "ids"
              },
              {
                "name": "product_category_id"
              },
              {
                "name": "order"
              },
              {
                "name": "limit"
              }
            ]
          }
        ]
      }
    }
  }
}

你會看到server實現(xiàn)了一個clients的查詢operation阻肩,參數(shù)為idsnamedob运授。第二個操作是products烤惊,在這里的參數(shù)是idsproduct_category_idorder吁朦、limit柒室。

最后

GraphQL可以讓我們定義更加便捷的查詢Server。如果你有興趣學習的話逗宜,我強烈的建議你可以讀一讀GraphQL的定義說明雄右,然后試著自己實現(xiàn)一個GraphQL server空骚。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市擂仍,隨后出現(xiàn)的幾起案子囤屹,更是在濱河造成了極大的恐慌,老刑警劉巖逢渔,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肋坚,死亡現(xiàn)場離奇詭異,居然都是意外死亡肃廓,警方通過查閱死者的電腦和手機智厌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盲赊,“玉大人铣鹏,你說我怎么就攤上這事“ⅲ” “怎么了诚卸?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長递礼。 經(jīng)常有香客問我惨险,道長,這世上最難降的妖魔是什么脊髓? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任辫愉,我火速辦了婚禮,結果婚禮上将硝,老公的妹妹穿的比我還像新娘恭朗。我一直安慰自己,他們只是感情好依疼,可當我...
    茶點故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布痰腮。 她就那樣靜靜地躺著,像睡著了一般律罢。 火紅的嫁衣襯著肌膚如雪膀值。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天误辑,我揣著相機與錄音沧踏,去河邊找鬼。 笑死巾钉,一個胖子當著我的面吹牛翘狱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播砰苍,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼潦匈,長吁一口氣:“原來是場噩夢啊……” “哼阱高!你這毒婦竟也來了?” 一聲冷哼從身側響起茬缩,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤赤惊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后寒屯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荐捻,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年寡夹,在試婚紗的時候發(fā)現(xiàn)自己被綠了处面。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡菩掏,死狀恐怖魂角,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情智绸,我是刑警寧澤野揪,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站瞧栗,受9級特大地震影響斯稳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜迹恐,卻給世界環(huán)境...
    茶點故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一挣惰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧殴边,春花似錦憎茂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瞒大。三九已至傀缩,卻和暖如春固翰,著一層夾襖步出監(jiān)牢的瞬間划咐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工租幕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留誉己,地道東北人侨舆。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓戒职,卻偏偏與公主長得像,于是被迫代替她去往敵國和親透乾。 傳聞我的和親對象是個殘疾皇子洪燥,可洞房花燭夜當晚...
    茶點故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容