GraphQL 基礎實踐

ThinkJS發表於2018-07-23

本次內容是基於之前分享的文字版,諾想看重點的話可以看之前的 PPT

什麼是 GraphQL

GraphQL 是一款由 Facebook 主導開發的資料查詢和操作語言, 寫過 SQL 查詢的同學可以把它想象成是 SQL 查詢語言,但 GraphQL 是給客戶端查詢資料用的。雖然這讓你聽起來覺得像是一款資料庫軟體,但實際上 GraphQL 並不是資料庫軟體。 你可以將 GraphQL 理解成一箇中介軟體,是連線客戶端和資料庫之間的一座橋樑,客戶端給它一個描述,然後從資料庫中組合出符合這段描述的資料返回。這也意味著 GraphQL 並不關心資料存在什麼資料庫上。

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

GraphQL 解決了什麼問題

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

我們以 IMDB 電影資訊詳情頁為例子,看看我們得需要什麼樣的 API 才能滿足 RESTful API 設計的要求。先來看看主頁面上都需要什麼資訊。

GraphQL 基礎實踐

可以看到頁面上由電影基本資訊,演員和評分/評論資訊組成,按照設計要求,我們需要將這三種資源放在不同 API 之下。首先是電影基本資訊,我們有 API /movie/:id,給定一個電影ID返回基本資訊資料。

假裝 GET 一下獲得一個 JSON 格式的資料:

{
  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 指向的連線請求下去,我們就能得到我們頁面上所需的所有資訊。

GET /api/movue/1/actor/1

{
  name: “Ben Affleck”,
  dob: “1971-01-26”,
  desc: “blablabla”,
  movies:[“https://api/movie/1”]
}
複製程式碼

GET /api/movie/1/reviews

[
  {
     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,還必須選擇至少其中的一個欄位。

GraphQL 基礎實踐

把我們所需要的欄位合在一起,我們把它稱之為某某的選擇集。上面的 namedescratings 合在一起則稱之為 movie 的選擇集,同理,moviemyQry 的選擇集。需要注意的是,在標量上使用不能使用選擇集這種操作,因為它已經是最後一層了。

GraphQL 基礎實踐

movie 的旁邊,name: "Manchester",這個代表著傳入 movie 的引數,引數名為 name 值為Manchester,利用這些引數向伺服器表達你所需的資料需要符合什麼條件。

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

GraphQL 基礎實踐

  • 操作型別:指定本請求體要對資料做什麼操作,類似與 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。

型別系統、標量型別、非空型別、引數

GraphQL 基礎實踐

先看右邊的 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)

GraphQL 基礎實踐

如果我們的某個欄位返回不止一個標量型別的資料,而是一組,則需要使用List型別宣告,在該標量型別兩邊使用中括號[]包圍即可,與 JavaScript 中陣列的寫法相同,而且返回的資料也將會是陣列型別。

需要注意的是[Movie]![Movie!]兩種寫法的含義是不同的:前者表示 movies欄位始終返回不可為空但Movie元素可以為空。後者表示movies中返回的 Movie 元素不能為空,但 movies欄位的返回是可以為空的。

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

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

前面的例子中,傳入的引數均為標量型別,那麼如果我們想傳入一個擁有複雜結構的資料該怎麼定義呢。答案是使用關鍵字input。其使用方法和type完全一致。

GraphQL 基礎實踐

根據本例中的 Schema 定義,我們在查詢 searchdata的引數必須為

{ term: "Deepwater Horizon" }
複製程式碼

別名(Alias)

GraphQL 基礎實踐

想象這麼一個頁面,我要列出兩個電影的資訊做對比,為了發揮 GraphQL 的優勢,我要同時查詢這兩部電影的資訊,在請求體中請求 movie 資料。前面我們說到,請求體決定了返回資料的結構。在資料返回前查出兩個 key 為 movie 的資料,合併之後由於 key 重複而只能拿到一條資料。那麼在這種情況下我們需要使用別名功能。

別名即為返回欄位使用另一個名字,使用方法也很簡單,只需要在請求體的欄位前面使用別名:的形式即可,返回的資料將會自動替換為該名稱。

GraphQL 基礎實踐

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

GraphQL 基礎實踐

在上面的例子中,我們需要對比兩部電影的資料。如果換作是硬體對比網站,需要查詢的硬體數量往往不止兩個。此時編寫冗餘的選擇集顯得非常的費勁、臃腫以及難維護。為了解決這個問題,我們可以使用片段功能。GraphQL 允許定義一段公用的選擇集,叫片段。定義片段使用 fragment name on Type 的語法,其中 name為自定義的片段名稱,Type為片段來自的型別。

本例中的請求體的選擇集公共部分提取成片段之後為

fragment movieInfo on Movie {
   name
   desc
}
複製程式碼

在正式使用片段之前,還需要向各位介紹片段解構功能。類似於 JavaScript 的結構。GraphQL 的片段結構符號將片段內的欄位“結構”到選擇集中。

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 的結構保持一致。

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

GraphQL 基礎實踐

回憶前面的資料模型定義,裡面的 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

GraphQL 基礎實踐

有請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;
      }
    );
  };
};
複製程式碼

GraphQL 基礎實踐

接下來引用到我們中介軟體的handle配置中,完美,大功告成,用 ThinkJS 搭建的 GraphQL 伺服器就此告一段落,npm start 執行起來之後,用 GraphiQL “播放”一下你的請求體(記得自己先往資料庫灌資料)。

GraphQL 基礎實踐

GraphQL 的優缺點

優點

  • 所見即所得:所寫請求體即為最終資料結構
  • 減少網路請求:複雜資料的獲取也可以一次請求完成
  • Schema 即文件:定義的 Schema 也規定了請求的規則
  • 型別檢查:嚴格的型別檢查能夠消除一定的認為失誤

缺點

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

完整原始碼可以在這裡找到,中介軟體可以在這裡找到

相關文章