GraphQL利器

前言

在過去的十多年中奏属,REST已經(jīng)成為設(shè)計(jì)web api的一個(gè)模糊標(biāo)準(zhǔn)藻肄。它提供了一套比較完整語義化結(jié)構(gòu)化的標(biāo)準(zhǔn),然后這種設(shè)計(jì)還是相對而已比較僵硬皆串,不夠靈活,不能快速的對API進(jìn)行擴(kuò)容眉枕,無法跟上目前客戶的快速需求變化恶复。然而Facebook提供了一套比較完整的API接口設(shè)計(jì)GraphQL,GraphQL解決了REST對于開發(fā)人員的一些不便,大大提供了開發(fā)效率齐遵,以及擴(kuò)展性寂玲。

官方定義:

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

什么是GraphQl

GraphQL全稱Graph Query Language 塔插,語義上指圖表化查詢語言梗摇,是一種描敘客戶端香服務(wù)器請求數(shù)據(jù)的API語法,類似于RESTful API規(guī)范想许,它與數(shù)據(jù)庫沒有任何關(guān)系伶授,
它不依賴任何數(shù)據(jù)庫,可以與任何的MySQL流纹、NoSQL數(shù)據(jù)庫一起使用糜烹。

GraphQl的特征

  • schema
    GraphQl schema是強(qiáng)類型的,可使用SDL來定義類似于數(shù)據(jù)庫的Schema定義漱凝。每一個(gè)schema中允許出現(xiàn)三種根類型:Query疮蹦、Mutation、Subscription茸炒,每一次調(diào)用GraphQL服務(wù)愕乎,需要指定嗲用Schema中的哪種根類型(默認(rèn)是Query)
    query是用來查詢數(shù)據(jù)、mutation是用來增加壁公、修改感论、刪除數(shù)據(jù)的
query {
  users(): [User!]!
  user(id: Int): User!
}

mutation {
  createUser(): User!
  deleteUser(id: Int): User!
}
  • Type

    是對于數(shù)據(jù)模型的抽象, 包含兩種類型:一種是scalar type(標(biāo)量類型) 紊册,另一種是object type(對象類型)比肄。

    • scalar type:

      GraphQL中的內(nèi)建的標(biāo)量包含,String、Int芳绩、Float掀亥、Boolean、Enum妥色,除此之外铺浇,GraphQL中可以通過scalar聲明一個(gè)新的標(biāo)量

    • object type
      一個(gè) GraphQL schema 中的最基本的組件是對象類型,如上面的User垛膝,它表示服務(wù)器將要返回的所有字段鳍侣,如:

    type User {
        id: ID! #id
        mobile: String #電話好嗎
        nickName: String #昵稱
        avatar:String #頭像
        createTime:Date #創(chuàng)建時(shí)間
        updateTime:Date #更新時(shí)間
        workList:[Work]
    }
    

User是一個(gè)GraphQL對象類型,可以理解就是一個(gè)object對象吼拥,里面有一些字段屬性倚聚, id、mobile凿可、nickName惑折、avatar就是scalar type,
createTime枯跑、updateTime是自定義的scalar type惨驶,workList就是一個(gè)object type
在每一次操作User查詢時(shí),只會出現(xiàn)定義的字段如id敛助、mobile等等粗卜,
id: ID!表示這個(gè)字段是非空,也就是GraphQL服務(wù)一定能保證這個(gè)字段有值纳击。[Work] 表示一個(gè)work對象的數(shù)組對象续扔。

  • Resolve 解析器

上述中的schema(query、mutation)是用來操作數(shù)據(jù)焕数,type是數(shù)據(jù)類型纱昧,而resolve就是說明如何執(zhí)行相關(guān)的(query、mutation)操作放回?cái)?shù)據(jù)的邏輯堡赔,
也是連接schema與type的
GraphQL中识脆,默認(rèn)有這樣的約定,Query(包括query善已、mutation灼捂、subscription)和與之對應(yīng)的Resolve是同名的,比如關(guān)于users(): [User!]!這個(gè)query雕拼,它的Resolve的名字必然叫做user

如:前端調(diào)用query語句時(shí):

query{

   users{
       id
       mobile
       workList{
        id
       }
   }
}

它的解析過程:

  • 第一次解析時(shí)纵东,當(dāng)前的的類型時(shí)query,所以我們定義一個(gè)同名的 query:users 的Resolver
  • 我們會使用query:user的Resolve獲取解析數(shù)據(jù)
  • 如id啥寇,mobile 第一層順利解析偎球,在解析workList時(shí)洒扎,我們會使用users:workList 的Resolve去解析
  • 然后解析出來workList中的id

概括總結(jié)GraphQL大體解析流程就是遇見一個(gè)Query之后,嘗試使用它的Resolver取值衰絮,
之后再對返回值進(jìn)行解析袍冷,這個(gè)過程是遞歸的,直到所有解析Field類型是Scalar Type(標(biāo)量類型)為止猫牡。
整個(gè)解析過程可以想象為一個(gè)很長的Resolver Chain(解析鏈)胡诗。

為什么使用GraphQL

  • GraphQL解決了什么問題

    • 接口多難維護(hù):接口的數(shù)量通常由業(yè)務(wù)場景的數(shù)量決定,為了盡量減少接口數(shù)量淌友,我們經(jīng)常對業(yè)務(wù)進(jìn)行抽象煌恢,即使如此,由于業(yè)務(wù)總是多變的
      露的接口還是很多。
    • 接口不夠靈活:出于帶寬的考慮移動端我們要求接口返回盡量少的字段,PC 端通常要展現(xiàn)更多字段欣鳖;
    • 接口合并:如考慮首屏性能,我們又要求對接口做合并柒室;傳統(tǒng) API 應(yīng)對這些需求,前后端都面臨改造,成本較高。
    • 文檔不全:由于接口文檔幾乎總是不能及時(shí)更新肴颊,前端工程師無法預(yù)知接口響應(yīng)的數(shù)據(jù)格式,影響前端開發(fā)進(jìn)度渣磷。
  • GraphQL如何解決問題

    • 接口多難維護(hù):
      GraphQL并不需要為每個(gè)業(yè)務(wù)提供一個(gè)對應(yīng)的接口婿着,只需要定義一些基本的schema即可

    • 接口不夠靈活:
      在GraphQL中,是client需要哪些數(shù)據(jù)幸海,server才精確返回哪些數(shù)據(jù)祟身。如:

       query{
          user(id:1):{
              id
              mobile
          }
       }
       
       // response 
       
       user:{
         id:1
         mobile:'139XXXXX139'
       }
        
      
         query{
            user(id:1):{
                id
                mobile
                name
            }
         }
         
         // response 
         
         user:{
           id:1
           mobile:'139XXXXX139'
           name:'YUX'
         }
          
      

      基于Resolve會解析query中所有的字段奥务,其他字段不會返回物独。

    • 接口合并減少網(wǎng)絡(luò)請求
      基于一些數(shù)據(jù)量大的接口,rest一般會是通過多個(gè)接口并/串行組合數(shù)據(jù)氯葬,而graphql則可以通過嵌套編寫復(fù)雜的schema達(dá)到一次請求

      {
          user(id:1) {
              id
              mobile
              workList(limit: 10) {
                  id
                  url
                  name
                  comment(limit: 20) {
                      id
                      author
                      title
                  }
              }
          }
      }
      
      

      如上挡篓,我們可以用一個(gè)借口獲得與用戶相關(guān)的所有信息,并不需要多個(gè)接口返回帚称。

    • 文檔不全
      GraphQL讓你的整個(gè)應(yīng)用共享一套API官研,通過GraphQL API能夠更好的利用你的現(xiàn)有數(shù)據(jù)和代碼,就是說通過我們定義的schema闯睹,很清晰的能看到
      入?yún)⒁约俺鰠⑾酚穑⒉恍枰^多的API文檔說明。

概括總結(jié):
客戶端的對數(shù)據(jù)的述求:調(diào)用哪個(gè)方法楼吃,傳遞什么樣的參數(shù)始花,返回哪些字段妄讯。服務(wù)端拿到這段 Schema 之后,
通過事先定義好的服務(wù)端 Schema 接收請求參數(shù)并執(zhí)行對應(yīng)的 resolve 函數(shù)提供數(shù)據(jù)服務(wù)酷宵。
整個(gè)過程可以想象成我們吃自助餐的過程亥贸,服務(wù)端 Schema 就好比自助餐線,
擺上我們能提供的所有食物浇垦;客戶端 Schema 就描述了我們想要吃的食物炕置,按需獲取就好了

GraphQL 潛在問題

  • N+1問題:在實(shí)現(xiàn)GraphQL代碼中我們很容易寫出性能比較差的查詢,引起N+1的問題
```
  works = Works.getAll();  
  works.forEach(item=>{
     label = Label.get(item.id)
  })
```

由于作品標(biāo)簽不存在work表中男韧,所有第一次查詢出作品列表朴摊,第二步循環(huán)去查詢每個(gè)作品的標(biāo)簽信息,原本是一個(gè)查詢的此虑,結(jié)果
導(dǎo)致來N+1次仍劈,這就是N+1問題,在graphql中寡壮,很容易造成這樣贩疙,主要是由于 GraphQL query 的逐層遞歸解析方式所引起的。
node解決方案采用DataLoader

  • 安全問題

    由于graphql自帶強(qiáng)大的內(nèi)省自檢機(jī)制也就是我們上面提到的靜態(tài)檢測况既,可以直接獲取后端定義的所有接口信息

     {
            __schema {
                types {
                    name
                }
            }
        }
        
        response:
        {
          "data": {
            "__schema": {
              "types": [
                {
                  "name": "Query"
                },
                {
                  "name": "Int"
                }]
              }
          }
        }
    

    所以對于每個(gè)接口的權(quán)限管理这溅,就需要開發(fā)者定制比較合理的鑒權(quán)機(jī)制

  • 拒絕服務(wù)
    如果我們定義如下查詢就會導(dǎo)致,服務(wù)器拒絕服務(wù)

      blogs(blogId: $blogId, systemType: $systemType) {
                _id
                title
                type
                content
                author {
                    name
                    blog {
                        author {
                            name
                            blog {
                                author {
                                    name
                                    blog {
                                        author {
                                            name
                                            blog {
    
                                                author {
                                                    name
                                                    # and so on...
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        title
                        createdAt
                        publishedAt
                    }
                }
                publishedAt
            }
    

    我們定義接口的時(shí)候盡量避免如此定義棒仍,也可以在GraphQL服務(wù)器上限制查詢深度悲靴,可以采用graphql-depth-limit解決該問題

  • 不易緩存

    官方的翻譯:在一個(gè)基于端點(diǎn)的API,客戶端能夠使用HTTP緩存輕易的避免重復(fù)獲取資源和識別什么時(shí)候兩個(gè)資源是一樣的莫其●校客戶端可以根據(jù)API中的URL作為全局唯一的標(biāo)識符構(gòu)建緩存。
    在GraphQL中乱陡,沒有類似URL的對象能夠作為全局唯一的標(biāo)識符浇揩。最佳實(shí)踐是提供這么一個(gè)標(biāo)識符供客戶端使用。

小結(jié)

GraphQL只是一個(gè)API技術(shù)憨颠,它為API連接的前后端提供了一種新的便捷處理方案胳徽,與語言無關(guān)。無論如何爽彤,該做鑒權(quán)的就鑒權(quán)养盗,該校驗(yàn)數(shù)據(jù)的還是一定得校驗(yàn)。
GraphQL會提供我們的開發(fā)效率适篙,減少前后端的溝通成本往核,提高了程序的可擴(kuò)展以及維護(hù)性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嚷节,一起剝皮案震驚了整個(gè)濱河市聂儒,隨后出現(xiàn)的幾起案子蝶缀,更是在濱河造成了極大的恐慌,老刑警劉巖薄货,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翁都,死亡現(xiàn)場離奇詭異,居然都是意外死亡谅猾,警方通過查閱死者的電腦和手機(jī)柄慰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來税娜,“玉大人坐搔,你說我怎么就攤上這事【淳兀” “怎么了概行?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長弧岳。 經(jīng)常有香客問我凳忙,道長,這世上最難降的妖魔是什么禽炬? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任涧卵,我火速辦了婚禮,結(jié)果婚禮上腹尖,老公的妹妹穿的比我還像新娘柳恐。我一直安慰自己,他們只是感情好热幔,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布乐设。 她就那樣靜靜地躺著,像睡著了一般绎巨。 火紅的嫁衣襯著肌膚如雪近尚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天认烁,我揣著相機(jī)與錄音肿男,去河邊找鬼。 笑死却嗡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嘹承。 我是一名探鬼主播窗价,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼叹卷!你這毒婦竟也來了撼港?” 一聲冷哼從身側(cè)響起坪它,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帝牡,沒想到半個(gè)月后往毡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡靶溜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年开瞭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罩息。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嗤详,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瓷炮,到底是詐尸還是另有隱情葱色,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布娘香,位于F島的核電站苍狰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏烘绽。R本人自食惡果不足惜舞痰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诀姚。 院中可真熱鬧响牛,春花似錦、人聲如沸赫段。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽糯笙。三九已至贬丛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間给涕,已是汗流浹背豺憔。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留够庙,地道東北人恭应。 一個(gè)月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像耘眨,于是被迫代替她去往敵國和親昼榛。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

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