GraphQL

一、什么是GraphQL

GraphQL 是一個(gè)用于 API 的查詢語言,是一個(gè)使用基于類型系統(tǒng)來執(zhí)行查詢的服務(wù)端運(yùn)行時(shí)(類型系統(tǒng)由你的數(shù)據(jù)定義)。GraphQL 并沒有和任何特定數(shù)據(jù)庫或者存儲(chǔ)引擎綁定,而是依靠你現(xiàn)有的代碼和數(shù)據(jù)支撐,GraphQL 可以運(yùn)行在任何后端框架或者編程語言之上。

GraphQL 全稱叫 Graph Query Language,官方宣傳語是“為你的 API 量身定制的查詢語言”。

用傳統(tǒng)的方式來解釋就是:相當(dāng)于將你所有后端 API 組成的集合看成一個(gè)數(shù)據(jù)庫,用戶終端發(fā)送一個(gè)查詢語句,你的 GraphQL 服務(wù)解析這條語句并通過一系列規(guī)則從你的“ API 數(shù)據(jù)庫”里面將查詢的數(shù)據(jù)結(jié)果返回給終端,而 GraphQL 就相當(dāng)于這個(gè)系統(tǒng)的一個(gè)查詢語言。

二、存在的問題:

REST API :服務(wù)端決定有哪些數(shù)據(jù)返回,客戶端只能挑選使用,如果數(shù)據(jù)過于冗余也只能默默接收再對(duì)數(shù)據(jù)進(jìn)行處理;而數(shù)據(jù)不能滿足需求則需要請(qǐng)求更多的接口。以上就是我們常說的“過渡獲取”和“欠缺獲取”

由于"過度"和"欠缺"的獲取問題及其對(duì)客戶端應(yīng)用程序性能的影響,促進(jìn)有效獲取的 API 技術(shù)才有機(jī)會(huì)在市場(chǎng)上引起轟動(dòng) —— GraphQL 大膽地介入并填補(bǔ)了這一空白。

舉個(gè)簡(jiǎn)單的例子

graphQL 查詢語句

{
  getCategories {
    id
    name
    products {
      id
      name
    }
  }
}

輸出結(jié)果

{
  "data": {
    "getCategories": [
      {
        "id": "1",
        "name": "吃的",
        "products": [
          {
            "id": "1",
            "name": "浪味仙"
          }
        ]
      },
      {
        "id": "2",
        "name": "喝的",
        "products": [
          {
            "id": "4",
            "name": "茶顏悅色"
          }
        ]
      }
    ]
  }
}

客戶端現(xiàn)在不想要商品信息了 只需要獲取商品分類,你只需要將你的查詢語句修改一下即可

{
  getCategories {
    id
    name
  }
}

輸出結(jié)果

{
  "data": {
    "getCategories": [
      {
        "id": "1",
        "name": "吃的"
      },
      {
        "id": "2",
        "name": "喝的"
      }
    ]
  }
}

現(xiàn)在我想要通過這一個(gè)查詢,獲取到商品分類,以及所有商品的信息

{
  getCategories {
    id
    name
   
  }
  getProducts {
    id
    name
  }
}

輸出結(jié)果

{
  "data": {
    "getCategories": [
      {
        "id": "1",
        "name": "吃的"
      },
      {
        "id": "2",
        "name": "喝的"
      }
    ],
    "getProducts": [
      {
        "id": "1",
        "name": "浪味仙"
      },
      {
        "id": "4",
        "name": "茶顏悅色"
      }
    ]
  }
}

是不是很方便,通過上面例子,可以發(fā)現(xiàn)GraphQL API有如下優(yōu)點(diǎn):客戶端可以自定義查詢語句,數(shù)據(jù)獲取靈活多變,服務(wù)端按需返回?cái)?shù)據(jù),減少網(wǎng)絡(luò)的開銷,提高了性能。而這些都是Restful API的弊端。

三、基礎(chǔ)概念

在介紹GraphQL的使用之前先要了解一些概念

1.GraphQL 類型系統(tǒng)(Type System)

類型系統(tǒng) 是整個(gè) GraphQL 的核心,它用來定義每個(gè)查詢對(duì)象和返回對(duì)象的類型。GraphQL的Type簡(jiǎn)單可以分為兩種,一種叫做Scalar Type(標(biāo)量類型),另一種叫做Object Type(對(duì)象類型)。

a)標(biāo)量類型(Scalar Types)

一個(gè)對(duì)象類型有自己的名字和字段,而某些時(shí)候,這些字段必然會(huì)解析到具體數(shù)據(jù)。這就是標(biāo)量類型的來源:它們表示對(duì)應(yīng) GraphQL 查詢的葉子節(jié)點(diǎn)。

標(biāo)量類型這個(gè)概念有點(diǎn)重要,這個(gè)字段是不是一個(gè)標(biāo)量類型,決定了我們這個(gè)查詢是不是還要繼續(xù)往更深層去遞歸查詢。

GraphQL 自帶一組默認(rèn)標(biāo)量類型:
GraphQLInt:有符號(hào) 32 位整數(shù)。
GraphQLFloat:有符號(hào)雙精度浮點(diǎn)值。
GraphQLString:UTF‐8 字符序列。
GraphQLBoolean 或者 false。
GraphQLID:ID 標(biāo)量類型表示一個(gè)唯一標(biāo)識(shí)符。

b)對(duì)象類型

一個(gè) GraphQL schema 中的最基本的組件是對(duì)象類型,表示你可以從服務(wù)上獲取到什么類型的對(duì)象,以及這個(gè)對(duì)象有什么字段。

例如 以下這段代碼中 id 、name 就是標(biāo)量類型。products就是一個(gè)對(duì)象類型

 type categories {
    id
    name
    products {
      id
      name
    }
b)Schema

在 GraphQL 中,類型的定義以及查詢本身都是通過 Schema 去定義的。
Schema它是用來描述接口獲取數(shù)據(jù)邏輯的。我們可以將Schema理解為多個(gè)Query組成的一張表。

這里又涉及一個(gè)新的概念Query,GraphQL中使用Query來抽象數(shù)據(jù)的查詢邏輯,當(dāng)前標(biāo)準(zhǔn)下,有三種查詢類型,分別是query(查詢)、mutation(更改)和subscription(訂閱)。

query(查詢):當(dāng)獲取數(shù)據(jù)時(shí),應(yīng)當(dāng)選取Query類型
mutation(更改):當(dāng)嘗試修改數(shù)據(jù)時(shí),應(yīng)當(dāng)使用mutation類型
subscription(訂閱):當(dāng)希望數(shù)據(jù)更改時(shí),可以進(jìn)行消息推送,使用subscription類型

c)Resolver

我們已經(jīng)了解了graphQL的類型系統(tǒng)和schema,那么我們的數(shù)據(jù)到底怎么來呢?答案是來自 Resolver 函數(shù)。

Resolver 的概念非常簡(jiǎn)單。Resolver 對(duì)應(yīng)著 Schema 上的字段,當(dāng)請(qǐng)求體查詢某個(gè)字段時(shí),對(duì)應(yīng)的 Resolver 函數(shù)會(huì)被執(zhí)行,由 Resolver 函數(shù)負(fù)責(zé)到數(shù)據(jù)庫中取得數(shù)據(jù)并返回,最終將請(qǐng)求體中指定的字段返回。

type Movie {
    name
    genre
}
type Query {
    movie: Movie!
}

當(dāng)請(qǐng)求體查詢movie時(shí),同名的 Resolver 必須返回Movie類型的數(shù)據(jù)。當(dāng)然你還可以單獨(dú)為name字段使用獨(dú)立的 Resolver 進(jìn)行解析。

以上是一些最基本的概念,最后我們來詳細(xì)的分析一下,graphQL是如何定義并查詢到數(shù)據(jù)的

服務(wù)端的schema:

//schema.js

let categories = [
    { id: '1', name: '吃的' },
    { id: '2', name: '喝的' },
]
let products = [
    { id: '1', name: '浪味仙', category: '1' },
    { id: '4', name: '茶顏悅色', category: '2' },
]
//分類里定義每個(gè)字段
const Category = new GraphQLObjectType({
    name: 'category',
    fields: () => ({
        id: { type: GraphQLString },
        name: { type: GraphQLString },
        products: {
            type: new GraphQLList(Product),//每個(gè)分類下是一個(gè)數(shù)組,而不是一個(gè)對(duì)象
            resolve(parent) {//parent代表上一層的查詢結(jié)果
                return products.filter(item => item.category === parent.id)
            }
        }
    })
})
//產(chǎn)品里定義每個(gè)字段
const Product = new GraphQLObjectType({
    name: 'product',
    fields: () => ({
        id: { type: GraphQLString },
        name: { type: GraphQLString }
    })
})
//查詢接口
const RootQuery = new GraphQLObjectType({
    name: 'root',
    fields: {
        getCategories: {
            type: new GraphQLList(Category),
            args: {
            },
            resolve(parent, args) {
                return categories
            }
        }
    }
})
module.exports = new GraphQLSchema({
    query: RootQuery,//查詢
    mutation: RootMutation//修改
})

1.以上是一個(gè)schema文件, 導(dǎo)出了一個(gè)GraphQLSchema的實(shí)例,GraphQLSchema接收兩個(gè)參數(shù),一個(gè)是query,一個(gè)是mutation。
2.RootQuery是一個(gè)GraphQLObjectType實(shí)例,name屬性非必填,這個(gè)屬性是最后生成接口文檔的query接口名字。
3.fields是解析函數(shù),在這里可以理解為查詢方法。
4.getCategories就是我們定義查詢的名稱了。
5.getCategories中有個(gè)type,定義了你使用getCategories能查詢到的所有字段。當(dāng)前查詢結(jié)果是一個(gè)GraphQLList類型,也就是一個(gè)數(shù)組類型,數(shù)組的每一項(xiàng)就是Category。
6.resolve中的內(nèi)容為查詢返回的數(shù)據(jù)。

客戶端對(duì)應(yīng)的查詢語法:

query{
  getCategories {
    id,
    name,
    products{
      id,
      name,
    }
  }
}

1.首先進(jìn)行第一層解析,查詢類型是query同時(shí)需要它的子query名是getCategories。
2.使用getCategories的Resolver獲取解析數(shù)據(jù),第一層解析完畢
3.之后對(duì)第一層解析的返回值,進(jìn)行第二層解析,當(dāng)前getCategories還包含3個(gè)屬性需要查詢,分別是id、name、product。
3.1 id和name在type Category中為標(biāo)量類型,直接返回getCategories中的對(duì)應(yīng)屬性值。id,name的解析結(jié)束。
3.2product在type Category類型中為對(duì)象類型,于是Granphql嘗試使用Category的Resolver獲取數(shù)據(jù),當(dāng)前field解析完畢。以此類推,直到所有的查詢值都為標(biāo)量的時(shí)候,查詢結(jié)束。
因此上例中的查詢結(jié)果為:

{
  "data": {
    "getCategories": [
      {
        "id": "1",
        "name": "吃的",
        "products": [
          {
            "id": "1",
            "name": "浪味仙"
          }
        ]
      },
      {
        "id": "2",
        "name": "喝的",
        "products": [
          {
            "id": "4",
            "name": "茶顏悅色"
          }
        ]
      }
    ]
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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