GraphQL 基礎實踐

本次內容是基於以前分享的文字版,諾想看重點的話能夠看以前的 PPTjavascript

什麼是 GraphQL

GraphQL 是一款由 Facebook 主導開發的數據查詢和操做語言, 寫過 SQL 查詢的同窗能夠把它想象成是 SQL 查詢語言,但 GraphQL 是給客戶端查詢數據用的。雖然這讓你聽起來以爲像是一款數據庫軟件,但實際上 GraphQL 並不是數據庫軟件。 你能夠將 GraphQL 理解成一箇中間件,是鏈接客戶端和數據庫之間的一座橋樑,客戶端給它一個描述,而後從數據庫中組合出符合這段描述的數據返回。這也意味着 GraphQL 並不關心數據存在什麼數據庫上。php

同時 GraphQL 也是一套標準,在這個標準下不一樣平臺不一樣語言有相應的實現。 GraphQL 中還設計了一套類型系統,在這個類型系統的約束下,能夠得到與 TypeScript 相近的相對安全的開發體驗。html

GraphQL 解決了什麼問題

咱們先來回顧一下咱們已經很是熟悉的 RESTful API 設計。簡單的說 RESTful API 主要是使用 URL 的方式表達和定位資源,用 HTTP 動詞來描述對這個資源的操做。vue

咱們以 IMDB 電影信息詳情頁爲例子,看看咱們得須要什麼樣的 API 才能知足 RESTful API 設計的要求。先來看看主頁面上都須要什麼信息。java

能夠看到頁面上由電影基本信息,演員和評分/評論信息組成,按照設計要求,咱們須要將這三種資源放在不一樣 API 之下。首先是電影基本信息,咱們有 API /movie/:id,給定一個電影ID返回基本信息數據。git

僞裝 GET 一下得到一個 JSON 格式的數據:github

{
  name: 「Manchester by the Sea」,
  ratings: 「PG-13」,
  score: 8.2,
  release: 「2016」,
  actors:[「https://api/movie/1/actor/1/」],
  reviews:[「https://api/movie/1/reviews」]
}
複製代碼

這裏麪包含了咱們所需的電影名、分級等信息,以及一種叫作 HyperMedia 的數據,一般是一個 URL,指明瞭可以獲取這個資源的 API 端點地址。若是咱們跟着 HyperMedia 指向的鏈接請求下去,咱們就能獲得咱們頁面上所需的全部信息。mongodb

GET /api/movue/1/actor/1數據庫

{
  name: 「Ben Affleck」,
  dob: 「1971-01-26」,
  desc: 「blablabla」,
  movies:[「https://api/movie/1」]
}
複製代碼

GET /api/movie/1/reviewsnpm

[
  {
     content: 「Its’s as good as…」,
     score: 9
  }
]
複製代碼

最後根據須要,咱們要將全部包含須要信息的 API 端點都請求一遍,對於移動端來講,發起一個 HTTP 請求仍是比較消耗資源的,特別是在一些網絡鏈接質量不佳的狀況下,一下發出多個請求反而會致使很差的體驗。

並且在這樣的 API 設計之中,特定資源分佈在特定的 API 端點之中,對於後端來講寫起來是挺方便的,但對於Web端或者客戶端來講並不必定。例如在 Android 或 iOS 客戶端上,發版升級了一個很爆炸的功能,同一個API上可能爲了支持這個功能而多吐一些數據。可是對於未升級的客戶端來講,這些新數據是沒有意義的,也形成了必定的資源浪費。若是單單將全部資源整合到一個 API 之中,還有可能會由於整合了無關的數據而致使數據量的增長。

而 GraphQL 就是爲了解決這些問題而來的,向服務端發送一次描述信息,告知客戶端所需的全部數據,數據的控制甚至能夠精細到字段,達到一次請求獲取全部所需數據的目的。

GraphQL Hello World

GraphQL 請求體

咱們先來看一下一個 GraphQL 請求長什麼樣:

query myQry ($name: String!) {
  movie(name: 「Manchester」) {
    name
    desc
    ratings
  }
}
複製代碼

這個請求結構是否是和 JSON 有那麼點類似?這是 Facebook 故意設計成這樣的,但願你讀完以後就能體會到 Facebook 的用心良苦了。

那麼,上面的這個請求描述稱爲一個 GraphQL 請求體,請求體即用來描述你要從服務器上取什麼數據用的。通常請求體由幾個部分組成,從裏到外瞭解一下。

首先是字段,字段請求的是一個數據單元。同時在 GraphQL 中,標量字段是粒度最細的一個數據單元了,同時做爲返回 JSON 響應數據中的最後一個字段。也就是說,若是是一個 Object,還必須選擇至少其中的一個字段。

把咱們所須要的字段合在一塊兒,咱們把它稱之爲某某的選擇集。上面的 namedescratings 合在一塊兒則稱之爲 movie 的選擇集,同理,moviemyQry 的選擇集。須要注意的是,在標量上使用不能使用選擇集這種操做,由於它已是最後一層了。

movie 的旁邊,name: "Manchester",這個表明着傳入 movie 的參數,參數名爲 name 值爲Manchester,利用這些參數向服務器表達你所需的數據須要符合什麼條件。

最後咱們來到請求體的最外層:

  • 操做類型:指定本請求體要對數據作什麼操做,相似與 REST 中的 GET POST。GraphQL 中基本操做類型有 query 表示查詢,mutation 表示對數據進行操做,例如增刪改操做,subscription 訂閱操做。
  • 操做名稱:操做名稱是個可選的參數,操做名稱對整個請求並不產生影響,只是賦予請求體一個名字,能夠做爲調試的依據。
  • 變量定義:在 GraphQL 中,聲明一個變量使用$符號開頭,冒號後面緊跟着變量的傳入類型。若是要使用變量,直接引用便可,例如上面的 movie 就能夠改寫成 movie(name: $name)

若是上述三者都沒有提供,那麼這個請求體默認會被視爲一個 query 操做。

請求的結果

若是咱們執行上面的請求體,咱們將會獲得以下的數據:

{
  "data": {
    "movie": {
      "name": "Manchester By the Sea",
      "desc": "A depressed uncle is asked to take care of his teenage nephew after the boy's father dies. ",
      "ratings": "R"
    }
  }
}
複製代碼

仔細對比結果和請求體的結構,你會發現,與請求體的結構是徹底一致的。也就是說,請求體的結構也肯定了最終返回數據的結構

GraphQL Server

在前面的 REST 舉例中,咱們請求多個資源有多個 API 端點。在 GraphQL 中,只有一個 API 端點,一樣也接受 GET 和 POST 動詞,如要操做 mutation 則使用 POST 請求。

前面還提到 GraphQL 是一套標準,怎麼用呢,咱們能夠藉助一些庫去解析。例如 Facebook 官方的 GraphQL.js。以及 Meteor 團隊開發的 Apollo,同時開發了客戶端和服務端,同時也支持流行的 Vue 和 React 框架。調試方面,可使用 Graphiql 進行調試,得益於 GraphQL 的類型系統和 Schema,咱們還能夠在 Graphiql 調試中使用自動完成功能。

Schema

前面咱們提到,GraphQL 擁有一個類型系統,那麼每一個字段的類型是怎麼約定的呢?答案就在本小節中。在 GraphQL 中,類型的定義以及查詢自己都是經過 Schema 去定義的。GraphQL 的 Schema 語言全稱叫 Schema Definition Language。Schema 自己並不表明你數據庫中真實的數據結構,它的定義決定了這整個端點能幹些什麼事情,肯定了咱們能向端點要什麼,操做什麼。再次回顧一下前面的請求體,請求體決定了返回數據的結構,而 Schema 的定義決定了端點的能力。

接下來咱們就經過一個一個的例子瞭解一下 Schema。

類型系統、標量類型、非空類型、參數

先看右邊的 Schema:type 是 GraphQL Schema 中最基本的一個概念,表示一個 GraphQL 對象類型,能夠簡單地將其理解爲 JavaScript 中的一個對象,在 JavaScript 中一個對象能夠包含各類 key,在 GraphQL 中,type 裏面一樣能夠包含各類字段(field),並且字段類型不只僅能夠是標量類型,還能夠是 Schema 中定義的其餘 type。例如上面的 Schema 中, Query 下的 movie 字段的類型就能夠是 Movie

在 GraphQL 中,有以下幾種標量類型:Int, Float, String, Boolean, ID ,分別表示整型、浮點型、字符串、布爾型以及一個ID類型。ID類型表明着一個獨一無二的標識,ID 類型最終會被轉化成String類型,但它必須是獨一無二的,例如 mongodb 中的 _id 字段就能夠設置爲ID類型。同時這些標量類型能夠理解爲 JavaScript 中的原始類型,上面的標量類型一樣能夠對應 JavaScript 中的 Number, Number, String, Boolean, Symbol

在這裏還要注意一點,type QueryQuery 類型是 Schema 中全部 query 查詢的入口,相似的還有 MutationSubscription,都做爲對應操做的入口點。

type Query下的 movie 字段中,咱們使用括號定義咱們能夠接受的參數名和參數的類型。在上面的 Schema 中,後面緊跟着的感嘆號聲明瞭此類型是個不可空類型(Non-Nullable),在參數中聲明表示該參數不能傳入爲空。若是感嘆號跟在 field 的後面,則表示返回該 type 的數據時,此字段必定不爲空。

經過上面的類型定義,能夠看到 GraphQL 中的類型系統起到了很重要的角色。在本例中,Schema 定義了 nameString類型,那麼你就不能傳 Int類型進去,此時會拋出類型不符的錯誤。一樣的,若是傳出的 ratings 數據類型不爲 String,也一樣會拋出類型不符的錯誤。

列表(List)、枚舉類型(Enum)

若是咱們的某個字段返回不止一個標量類型的數據,而是一組,則須要使用List類型聲明,在該標量類型兩邊使用中括號[]包圍便可,與 JavaScript 中數組的寫法相同,並且返回的數據也將會是數組類型。

須要注意的是[Movie]![Movie!]兩種寫法的含義是不一樣的:前者表示 movies字段始終返回不可爲空但Movie元素能夠爲空。後者表示movies中返回的 Movie 元素不能爲空,但 movies字段的返回是能夠爲空的。

你可能在請求體中注意到,genre 參數的值沒有被雙引號括起來,也不是任何內置類型。看到 Schema 定義,COMEDY是枚舉類型MovieTypes中的枚舉成員。枚舉類型用於聲明一組取值常量列表,若是聲明瞭某個參數爲某個枚舉類型,那麼該參數只能傳入該枚舉類型內限定的常量名。

傳入複雜結構的參數(Input)

前面的例子中,傳入的參數均爲標量類型,那麼若是咱們想傳入一個擁有複雜結構的數據該怎麼定義呢。答案是使用關鍵字input。其使用方法和type徹底一致。

根據本例中的 Schema 定義,咱們在查詢 searchdata的參數必須爲

{ term: "Deepwater Horizon" }
複製代碼

別名(Alias)

想象這麼一個頁面,我要列出兩個電影的信息作對比,爲了發揮 GraphQL 的優點,我要同時查詢這兩部電影的信息,在請求體中請求 movie 數據。前面咱們說到,請求體決定了返回數據的結構。在數據返回前查出兩個 key 爲 movie 的數據,合併以後因爲 key 重複而只能拿到一條數據。那麼在這種狀況下咱們須要使用別名功能。

別名即爲返回字段使用另外一個名字,使用方法也很簡單,只須要在請求體的字段前面使用別名:的形式便可,返回的數據將會自動替換爲該名稱。

片斷(Fragment)、片斷解構(Fragment Spread)

在上面的例子中,咱們須要對比兩部電影的數據。若是換做是硬件對比網站,須要查詢的硬件數量每每不止兩個。此時編寫冗餘的選擇集顯得很是的費勁、臃腫以及難維護。爲了解決這個問題,咱們可使用片斷功能。GraphQL 容許定義一段公用的選擇集,叫片斷。定義片斷使用 fragment name on Type 的語法,其中 name爲自定義的片斷名稱,Type爲片斷來自的類型。

本例中的請求體的選擇集公共部分提取成片斷以後爲

fragment movieInfo on Movie {
   name
   desc
}
複製代碼

在正式使用片斷以前,還須要向各位介紹片斷解構功能。相似於 JavaScript 的結構。GraphQL 的片斷結構符號將片斷內的字段「結構」到選擇集中。

接口(Interface)

與其餘大多數語言同樣,GraphQL 也提供了定義接口的功能。接口指的是 GraphQL 實體類型自己提供字段的集合,定義一組與外部溝通的方式。使用了 implements的類型必須包含接口中定義的字段。

interface Basic {
    name: String!
    year: Number!
}

type Song implements Basic {
    name: String!
    year: Number!
    artist: [String]!
}

type Video implements Basic {
    name: String!
    year: Number!
    performers: [String]!
}

Query {
    search(term: String!): [Basic]!
}
複製代碼

在本例中,定義了一個Basic接口,Song以及Video類型都要實現該接口的字段。而後在search查詢中返回該接口。

searchMedia查詢返回一組Basic接口。因爲該接口中的字段是全部實現了該接口的類型所共有的,在請求體上能夠直接使用。而對於特定類型上的其餘非共有字段,例如Video中的performers,直接選取是會有問題的,由於searchMedia在返回的數據中類型多是全部實現了該接口的類型,而在 Song類型中就沒有performers字段。此時咱們能夠藉助內聯片斷的幫助(下面介紹)。

聯合類型(Union)

聯合類型與接口概念差很少相同,不一樣之處在於聯合類型下的類型之間沒有定義公共的字段。在 Union 類型中必須使用內聯片斷的方式查詢,緣由與上面的接口類型一致。

union SearchResult = Song | Video
Query {
    search(term: String!): [SearchResult]!
}
複製代碼

內聯片斷(Inline Fragment)

對接口或聯合類型進行查詢時,因爲返回類型的不一樣致使選取的字段可能不一樣,此時須要經過內聯片斷的方式決定在特定類型下使用特定的選擇集。內聯選擇集的概念和用法與普通片斷基本相同,不一樣的是內聯片斷直接聲明在選擇集內,而且不須要fragment聲明。

查詢接口的例子:

query {
    searchMedia(term: "AJR") {
    	name
    	year
    	
        ...on Song {
            artist
        }
        
        ...on Video {
            performers
        }
    }
}

複製代碼

首選咱們須要該接口上的兩個公共字段,而且結果爲Song類型時選取artist字段,結果爲Video類型時選取performers字段。下面查詢聯合類型的例子也是同樣的道理。

查詢聯合類型的例子:

query {
    searchStats(player: "Aaron") {
        ...on NFLScore {
            YDS
            TD
        }
        
        ...on MLBScore {
            ERA
            IP
        }
    }
}

複製代碼

GraphQL 內置指令

GraphQL 中內置了兩款邏輯指令,指令跟在字段名後使用。

@include

當條件成立時,查詢此字段

query {
    search {
        actors @include(if: $queryActor) {
            name
        }
    }
}

複製代碼

@skip

當條件成立時,不查詢此字段

query {
    search {
        comments @skip(if: $noComments) {
            from
        }
    }
}

複製代碼

Resolvers

前面咱們已經瞭解了請求體以及 Schema,那麼咱們的數據到底怎麼來呢?答案是來自 Resolver 函數。

Resolver 的概念很是簡單。Resolver 對應着 Schema 上的字段,當請求體查詢某個字段時,對應的 Resolver 函數會被執行,由 Resolver 函數負責到數據庫中取得數據並返回,最終將請求體中指定的字段返回。

type Movie {
    name
    genre
}

type Query {
    movie: Movie!
}

複製代碼

當請求體查詢movie時,同名的 Resolver 必須返回Movie類型的數據。固然你還能夠單獨爲name字段使用獨立的 Resolver 進行解析。後面的代碼例子中將會清楚地瞭解 Resolver。

使用 ThinkJS 搭建 GraphQL API

ThinkJS 是一款面向將來開發的 Node.js 框架,整合了大量的項目最佳實踐,讓企業級開發變得如此簡單、高效。 框架底層基於 Koa 2.x 實現,兼容 Koa 的全部功能。

本例中咱們將使用 ThinkJS 配合 MongoDB 進行搭建 GraphQL API,ThinksJS 的簡單易用性會讓你愛不釋手!

快速安裝

首先安裝 ThinkJS 腳手架 npm install -g think-cli

使用 CLI 快速建立項目 thinkjs new gqldemo

切換到工程目錄中 npm install && npm start

不到兩分鐘,ThinkJS 服務端就搭建完了,so easy!

配置 MongoDB 數據庫

因爲本人比較喜歡 mongoose,恰好 ThinkJS 官方提供了 think-mongoose 庫快速使用,安裝好以後咱們須要在 src/config/extend.js中引入並加載該插件。

const mongoose = require('think-mongoose');
module.exports = [mongoose(think.app)];

複製代碼

接下來,在 adapter.js 中配置數據庫鏈接

export.model = {
    type: 'mongoose',
    mongoose: {
        connectionString: 'mongodb://你的數據庫/gql',
        options: {}
    }
};

複製代碼

如今,咱們在整個 ThinkJS 應用中都擁有了 mongoose 實例,看看還差啥?數據模型!

藉助 ThinkJS 強大的數據 模型功能,咱們只須要以數據集合的名稱做爲文件名創建文件並定義模型便可使用,相比 mongoose 原生的操做更爲簡單。

本例中咱們實現 actor 和 movie 兩組數據,在 model 目錄下分別創建 actor.jsmovie.js,並在裏面定義模型。

actor.js

module.exports = class extends think.Mongoose {
  get schema() {
    return {
      name: String,
      desc: String,
      dob: String,
      photo: String,
      addr: String,
      movies: [
        {
          type: think.Mongoose.Schema.Types.ObjectId,
          ref: 'movie'
        }
      ]
    };
  }
};

複製代碼

movie.js

module.exports = class extends think.Mongoose {
  get schema() {
    return {
      name: String,
      desc: String,
      ratings: String,
      score: Number,
      release: String,
      cover: String,
      actors: [
        {
          type: think.Mongoose.Schema.Types.ObjectId,
          ref: 'actor'
        }
      ]
    };
  }
};

複製代碼

處理 GraphQL 請求的中間件

要處理 GraphQL 請求,咱們就必須攔截特定請求進行解析處理,在 ThinkJS 中,咱們徹底能夠藉助中間件的能力完成解析和數據返回。中間件的配置在 middleware.js中進行。

ThinkJS 中配置中間件有三個關鍵參數:

  • match: 用於匹配 URL,咱們想讓咱們的請求發送到 /graphql 中進行處理,那麼咱們對這個路徑進行 match 後進行處理;
  • handle:中間件的處理函數,當 match 到時,此處理函數會被調用執行,咱們的解析任務也在這裏進行,並將解析結果返回;
  • options:options 時傳給中間件的參數,咱們能夠在此將咱們的 Schema 等內容傳給解析器使用。

咱們的中間件配置大概長這樣:

{
    match: '/graphql',
    handle: () => {},
    options: {}
}

複製代碼

解析 GraphQL 的核心

Apollo Server

Apollo Server 是一款構建在 Node.js 基礎上的 GraphQL 服務中間件,其強大的兼容性以及卓越的穩定性是本文選取此中間件的首要因素。

儘管 Apollo Server 沒有 ThinkJS 版的中間件,可是萬變不離其宗,咱們能夠經過 Apollo Server Core 中的核心方法 runHttpQuery 進行解析。

將它安裝到咱們的項目中: npm install apollo-server-core graphql --save

編寫中間件

runHttpQuery主要接受兩個參數,第一個是 GraphQLServerOptions,這個咱們能夠不須要配置,留空數組便可;第二個是HttpQueryRequest對象,咱們至少須要包含 methods,options以及query

他們分別表示當前請求的方法,GraphQL服務配置以及請求體。

而GraphQL服務配置中咱們至少要給出 schemaschema 應該是一個 GraphQLSchema實例,對於咱們前面例子中直接寫的 Schema Language,是不能被識別的,此時咱們須要藉助 graphql-tools 中的 makeExecutableSchema 工具將咱們的 Schema 和 Resolvers 進行關聯成 GraphQLSchema實例。

將它安裝到咱們的項目中:npm install graphql-tools --save

編寫 Schema 和 Resolver

在轉換成 GraphQLSchema 以前,首先要將咱們的 Schema 和 Resolver 準備好。

運用前面所學的知識,咱們能夠很快的編寫出一個簡單的 Schema 提供查詢演員信息和電影信息的接口。

type Movie {
  name: String!
  desc: String!
  ratings: String!
  score: Float!
  cover: String!
  actors: [Actor]
}

type Actor {
  name: String!
  desc: String!
  dob: String!
  photo: String!
  movies: [Movie]
}

type Query {
  movie(name: String!): [Movie]
  actor(name: String!): [Actor]
}

複製代碼

接下來,分別編寫解析 Querymovieactor字段的 Resolver 函數。

const MovieModel = think.mongoose('movie');
const ActorModel = think.mongoose('actor');

module.exports = {
    Query: {
        movie(prev, args, context) {
          return MovieModel.find({ name: args.name })
            	.sort({ _id: -1 })
            	.exec();
        },
        actor(prev, args, context) {
          return ActorModel.find({ name: args.name })
            	.sort({ _id: -1})
            	.exec();
        }
    }
}

複製代碼

爲了可以和 Schema 正確關聯,Resolver 函數的結構須要與 Schema 的結構保持一致。

到達這一步,有沒有發現什麼不對呢?

回憶前面的數據模型定義,裏面的 moviesactors 字段是一組另外一個集合中數據的引用,目的是方便創建電影和演員信息之間的關係以及維護,在 Resolver 運行以後,moviesactors 字段獲得的是一組 id,不符合 Schema 的定義,此時 GraphQL 會拋出錯誤。

那麼這個問題怎麼解決呢?前面講到 Resolver 的時候說到,每一個字段均可以對應一個 Resolver 函數,咱們分別對 moviesactors 字段設置 Resolver 函數,將上一個 Resolver 解析出來的 id 查詢一遍得出結果,最終返回的數據就能符合 Schema 的定義了。

const MovieModel = think.mongoose('movie');
const ActorModel = think.mongoose('actor');

module.exports = {
    Query: {
        movie(prev, args, context) {
          return MovieModel.find({ name: args.name })
            	.sort({ _id: -1 })
            	.exec();
        },
        actor(prev, args, context) {
          return ActorModel.find({ name: args.name })
            	.sort({ _id: -1})
            	.exec();
        }
    },
    Actor: {
        movies(prev, args, context) {
            return Promise.all(
            	prev.movies.map(_id => MovieModel.findOne({ _id }).exec())
            );
        }
    },
    Movie: {
        actors(prev, args, context) {
            return Promise.all(
            	prev.actors.map(_id => ActorModel.findOne({ _id }).exec())
            );
        }
    }
}

複製代碼

其中用到的 prev 參數就是上一個 Resolver 解析出的數據。

組合成 GraphQLSchema 實例

有了 Schema 和 Resolver 以後,咱們終於能夠把它們變成一個 GraphQLSchema 實例了。

調用 graphql-tools 中的 makeEcecutableSchema 進行組合好,放在 options 裏面稍後使用。

此時咱們的中間長這樣:

const { makeExecutableSchema } = require('graphql-tools');
const Resolvers = require('./resolvers'); // 咱們剛寫的 Resolver
const Schema = require('./schema'); // 咱們剛寫的 Schema
module.exports = {
    match: '/graphql',
    handle: () => {},
    options: {
        schema: makeExecutableSchema({
            typeDefs: Schema,
            resolvers: Resolvers
        })
    }
}
複製代碼
編寫 handler

有請apollo-server-core 裏面的runHttpQuery出場!

const { runHttpQuery } = require('apollo-server-core');

複製代碼

參照 apollo-server-koa,快速構建出 ThinkJS 版的 apollo-server 中間件。

const { runHttpQuery } = require('apollo-server-core');
module.exports = (options = {}) => {
  return ctx => {
    return runHttpQuery([ctx], {
      method: ctx.request.method,
      options,
      query:
        ctx.request.method === 'POST'
          ? ctx.post()
          : ctx.param()
    }).then(
      rsp => {
        ctx.set('Content-Type', 'application/json');
        ctx.body = rsp;
      },
      err => {
        if (err.name !== 'HttpQueryError') throw err;

        err.headers &&
          Object.keys(err.headers).forEach(header => {
            ctx.set(header, err.headers[header]);
          });

        ctx.status = err.statusCode;
        ctx.body = err.message;
      }
    );
  };
};
複製代碼

接下來引用到咱們中間件的handle配置中,完美,大功告成,用 ThinkJS 搭建的 GraphQL 服務器就此告一段落,npm start 運行起來以後,用 GraphiQL 「播放」一下你的請求體(記得本身先往數據庫灌數據)。

GraphQL 的優缺點

優勢

  • 所見即所得:所寫請求體即爲最終數據結構
  • 減小網絡請求:複雜數據的獲取也能夠一次請求完成
  • Schema 即文檔:定義的 Schema 也規定了請求的規則
  • 類型檢查:嚴格的類型檢查可以消除必定的認爲失誤

缺點

  • 增長了服務端實現的複雜度:一些業務可能沒法遷移使用 GraphQL,雖然可使用中間件的方式將原業務的請求進行代理,這無疑也將增長複雜度和資源的消耗

完整源代碼能夠在這裏找到,中間件能夠在這裏找到

相關文章
相關標籤/搜索