GraphQl深入講解和Express集成

GraphQl 介紹

GraphQL 是一種新的 API 的查詢語言,它提供了一種更高效、強(qiáng)大和靈活 API 查詢。它 是由 Facebook 開發(fā)和開源,目前由來自世界各地的大公司和個(gè)人維護(hù)。 GraphQL 對你的 API 中的數(shù)據(jù)提供了一套易于理解的完整描述,使得客戶端能夠準(zhǔn)確地獲得它需要的數(shù)據(jù),而且 沒有任何冗余。它彌補(bǔ)了 RESTfulAPI(字段冗余,擴(kuò)展性差、無法聚合 api、無法定義數(shù)據(jù) 類型、網(wǎng)絡(luò)請求次數(shù)多)等不足。

注意:GraphQL 是 api 的查詢語言,而不是數(shù)據(jù)庫。從這個(gè)意義上說,它是數(shù)據(jù)庫無關(guān)的, 而且可以在使用 API 的任何環(huán)境中有效使用,我們可以理解為 GraphQL 是基于 API 之上的一 層封裝,目的是為了更好,更靈活的適用于業(yè)務(wù)的需求變化。

  1. GraphQL 可以用在常見各種服務(wù)器端語言以及客戶端語言中
    服務(wù)器端語言:C#/.NET、Clojure、Elixir、Erlang、Go、Groovy、 Java、 JavaScript、PHP、Python、 Scala、Ruby

客戶端語言: js、 React+ReactNative、 Angular、 Vue.js、 ApolloLink、NativeiOS、NativeAndroid、 Scala.js

中文文檔

Github

  1. GraphQL 出現(xiàn)的歷史背景

當(dāng)提起API設(shè)計(jì)的時(shí)候,大家通常會(huì)想到SOAP(一種簡單的基于 XML 的協(xié)議)
, RESTful 等設(shè)計(jì)方式,從 2000 年 RESTful 的理論被提出的時(shí)候,在業(yè)界引起了很大反響,因?yàn)檫@種 設(shè)計(jì)理念更易于用戶的使用,所以便很快的被大家所接受。我們知道 REST 是一種從服務(wù) 器公開數(shù)據(jù)的流行方式。當(dāng) REST 的概念被提及出來時(shí),客戶端應(yīng)用程序?qū)?shù)據(jù)的需求相 對簡單,而開發(fā)的速度并沒有達(dá)到今天的水平。因此 REST 對于許多應(yīng)用程序來說是非常 適合的。然而在業(yè)務(wù)越發(fā)復(fù)雜,客戶對系統(tǒng)的擴(kuò)展性有了更高的要求時(shí),API 環(huán)境發(fā)生了巨 大的變,RESTful 顯得心有余而力不足。比如:字段冗余,擴(kuò)展性差、無法聚合 api、無法 定義數(shù)據(jù)類型、網(wǎng)絡(luò)請求次數(shù)多。

GraphQL 的出現(xiàn)整好彌補(bǔ)了 RESTfulAPi 的不足。

RESTfulAPI 不足

  1. 擴(kuò)展性(多個(gè)終端需要返回不同的字段),單個(gè)RESTful接口返回?cái)?shù)據(jù)越來越
    臃腫。前端對于真正用到的字段是沒有直觀映像的,僅僅通過url地址,無法預(yù)測也無
    法回憶返回的字段數(shù)目和字段是否有效,接口返回50個(gè)字段,但卻只用5個(gè)字段,造
    成字段冗余,擴(kuò)展性差,單個(gè)RESTful接口返回?cái)?shù)據(jù)越來越臃腫。
  2. API聚合問題,某個(gè)前端展現(xiàn),實(shí)際需要調(diào)用多個(gè)獨(dú)立的 RESTful API 才能獲
    取到足夠的數(shù)據(jù),導(dǎo)致網(wǎng)絡(luò)請求次數(shù)多
  3. 前后端字段頻繁改動(dòng),導(dǎo)致類型不一致,錯(cuò)誤的數(shù)據(jù)類型可能會(huì)導(dǎo)致網(wǎng)站出錯(cuò)
    尤其是在業(yè)務(wù)多變的場景中,很難在保證工程質(zhì)量的同時(shí)快速滿足業(yè)務(wù)需求

GraphQL 的優(yōu)點(diǎn)

  1. 吸收了RESTful API的特性。
  2. 所見即所得
    各種不同的前端框架和平臺(tái)可以指定自己需要的字段。查詢的返回結(jié)果就是輸
    入的查詢結(jié)構(gòu)的精確映射
  3. 客戶端可以自定義Api聚合。
    如果設(shè)計(jì)的數(shù)據(jù)結(jié)構(gòu)是從屬的,直接就能在查詢語句中指定;即使數(shù)據(jù)結(jié)構(gòu)是獨(dú)
    立的,也可以在查詢語句中指定上下文,只需要一次網(wǎng)絡(luò)請求,就能獲得資源和子
    資源的數(shù)據(jù)。
  4. 代碼即是文檔
    GraphQL 會(huì)把schema定義和相關(guān)的注釋生成可視化的文檔,從而使得代碼
    的變更,直接就反映到最新的文檔上,避免RESTful中手工維護(hù)可能會(huì)造成代碼、文檔不一致的問題。
  5. 參數(shù)類型強(qiáng)校驗(yàn)
    RESTful方案本身沒有對參數(shù)的類型做規(guī)定,往往都需要自行實(shí)現(xiàn)參數(shù)的校驗(yàn)機(jī)制,
    以確保安全。
    但GraphQL提供了強(qiáng)類型的schema機(jī)制,從而天然確保了參數(shù)類型的合法性。

Express集成GraphQl

  1. 找到 express-graphql 官方文檔

https://github.com/graphql/express-graphql

  1. 安裝 express-graphql graphql

npm install express-graphql graphql--save

  1. 引入 express-graphql 配置中間件
var express=require('express'); /*引入*/
const graphqlHTTP = require('express-graphql');
var GraphQLSchema=require('./schema/default.js');
var app=express(); /*實(shí)例化*/
app.use('/graphql', graphqlHTTP({
schema: GraphQLSchema, graphiql: true
}));
app.get('/',function(req,res){
res.send('你好 express11');
})
app.listen(3000,'127.0.0.1');
  1. 定義 GraphQLSchema
  • 新建 schema/default.js
  • 定義 Schema
const DB = require('../model/db.js');
const {
    GraphQLObjectType, GraphQLString, GraphQLInt, GraphQLSchema, GraphQLList
} = require('graphql');
//定義導(dǎo)航 Schema 類型
var GraphQLNav = new GraphQLObjectType({
    name: 'nav', fields: {
        title: { type: GraphQLString }, url: { type: GraphQLString }, sort: { type: GraphQLInt }, status: { type: GraphQLInt }, add_time: { type: GraphQLString }
    }
})
//定義根 根里面定義調(diào)用對應(yīng)導(dǎo)航 Schema 類型的方法
var Root = new GraphQLObjectType({
    name: "RootQueryType", fields: {
        navList: {
            type: GraphQLList(GraphQLNav), async resolve(parent, args) {
                var navList = await DB.find('nav', {});
                console.log(navList)
                return navList;
            }
        }
    }
})
//掛載根
module.exports = new GraphQLSchema({
    query: Root
});

GraphQl 類型系統(tǒng)

可以將 GraphQL 的類型系統(tǒng)分為標(biāo)量類型(Scalar Types,標(biāo)量類型)和其他高級(jí)數(shù)據(jù)類型,標(biāo)量類型即可以表示最細(xì)粒度數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)類型,可以和 JavaScript 的原始類型對應(yīng)。

GraphQL 規(guī)范目前規(guī)定支持的標(biāo)量類型有

Int:有符號(hào) 32 位整數(shù)。

  • Float:有符號(hào)雙精度浮點(diǎn)值。
  • String:UTF‐8 字符序列。
  • Boolean:true 或者 false。
  • ID:ID 標(biāo)量類型表示一個(gè)唯一標(biāo)識(shí)符,通常用以重新獲取對象或者作為緩存中的鍵。

ID 類型使用和 String 一樣的方式序列化;但是針對主鍵id時(shí)候,例如mongodb不需要getObjectID即可直接使用

GraphQL 其他高級(jí)數(shù)據(jù)類型包括

  1. Object :對象

用于描述層級(jí)或者樹形數(shù)據(jù)結(jié)構(gòu)。對于樹形數(shù)據(jù)結(jié)構(gòu)來說,葉子字段的類型都是標(biāo)量數(shù)據(jù)類型。幾乎所有 GraphQL 類型都是對象類型。Object 類型有一個(gè) name 字段,以及一個(gè)很重要的 fields 字段。fields 字段可以描述出一個(gè)完整的數(shù)據(jù)結(jié)構(gòu)。例如一個(gè)表示地址數(shù)據(jù)結(jié)構(gòu)的 GraphQL 對象為。

const AddressType = new GraphQLObjectType({
    name: 'Address',
    fields: {
        street: { type: GraphQLString }, number: { type: GraphQLInt },
        formatted: {
            type: GraphQLString, resolve(obj) {
                return obj.number + ' ' + obj.street
            }
        }
    }
});
  1. Interface :接口用于描述多個(gè)類型的通用字
  2. Union :聯(lián)合類型用于描述某個(gè)字段能夠支持的所有返回類型以及具體請求真正的返回類型
  3. Enum :枚舉用于表示可枚舉數(shù)據(jù)結(jié)構(gòu)的類型
  4. Input Object :輸入對象
  5. List :列表

列表是其他類型的封裝,通常用于對象字段的描述。例如下面PersonType 類型數(shù)據(jù)的parents 和 children 字段:

const PersonType = new GraphQLObjectType({
    name: 'Person',
    fields: () => ({
        parents: { type: new GraphQLList(Person) },
        children: { type: new GraphQLList(Person) },
    })
});
  1. Non-Null :不能為 Null

Non-Null 強(qiáng)制類型的值不能為 null,并且在請求出錯(cuò)時(shí)一定會(huì)報(bào)錯(cuò)??梢杂糜诒仨毐WC值不能為 null 的字段。例如數(shù)據(jù)庫的行的 id 字段不能為 null:

const RowType = new GraphQLObjectType({
    name: 'Row',
    fields: () => ({
        id: { type: new GraphQLNonNull(GraphQLString) }
    })
});

GraphQl 查詢語言

GraphQL 規(guī)范支持兩種操作:

  • query:僅獲取數(shù)據(jù)(fetch)的只讀請求
  • mutation:獲取數(shù)據(jù)后還有寫操作的請求
    新版本的 GraphQL 還支持 subscription ,這是為了處理訂閱更新(類似于消息隊(duì)列)這種比較復(fù)雜的實(shí)時(shí)數(shù)據(jù)更新場景而設(shè)計(jì)的操作。


    圖一.png

    圖二.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容