什么是GraphQL
GraphQL是比REST更高效、強(qiáng)大和靈活的新一代API標(biāo)準(zhǔn)。Facebook開(kāi)發(fā)了GraphQL并且將其開(kāi)源,目前其由一大群來(lái)自全球各地的公司和個(gè)人維護(hù)恕出。
注意到GraphQL是API標(biāo)準(zhǔn),不要看到QL結(jié)尾就以為其是一種數(shù)據(jù)庫(kù)技術(shù)浙巫。
比REST更靈活的一種選擇
REST是目前比較流行的一種暴露服務(wù)端數(shù)據(jù)的常見(jiàn)方式刷后,其簡(jiǎn)化了客戶(hù)端尤其是移動(dòng)端和服務(wù)器交互的流程。但是隨著業(yè)務(wù)變得復(fù)雜尝胆,有些情況變得棘手:
移動(dòng)端數(shù)量的增多,對(duì)數(shù)據(jù)的效率要求變高
移動(dòng)端和PC端相比护桦,是需要提高對(duì)數(shù)據(jù)獲取的效率的含衔,這個(gè)效率就是說(shuō)要減少網(wǎng)絡(luò)請(qǐng)求、要減少無(wú)用數(shù)據(jù)的傳輸二庵。
應(yīng)對(duì)復(fù)雜的前端框架和平臺(tái)
現(xiàn)在的情況是僅維護(hù)一套API來(lái)應(yīng)對(duì)不同框架和平臺(tái)的請(qǐng)求贪染。PC端一個(gè)頁(yè)面比移動(dòng)端一個(gè)頁(yè)面展示的內(nèi)容要多很多,之前后端提供給PC端的API如果直接提供給移動(dòng)端來(lái)使用勢(shì)必造成資源浪費(fèi)催享。所以移動(dòng)端的人會(huì)去找后端的人干一架杭隙,結(jié)果要么是后端再給移動(dòng)端單獨(dú)寫(xiě)一套API,要么就是移動(dòng)端忍受著API請(qǐng)求返回?cái)?shù)據(jù)中存在大量冗余的數(shù)據(jù)因妙。
需要更快速地迭代更新
互聯(lián)網(wǎng)時(shí)代最大的特色除了加班也許就是快了痰憎。好多公司在喊著小步快跑、快速試錯(cuò)攀涵,畢竟市場(chǎng)不等人信殊。然而REST標(biāo)準(zhǔn)的API似乎很難快速地跟上這快跑的節(jié)奏。也許一個(gè)API剛出來(lái)汁果,產(chǎn)品那邊已經(jīng)改了原型涡拘,界面重新設(shè)計(jì)了。這時(shí)候就要麻煩后端同學(xué)加個(gè)班把接口改一下吧据德。
誰(shuí)在用GraphQL
一個(gè)產(chǎn)品的流行鳄乏,肯定是解決了目前的某些痛點(diǎn)。雖然GraqhQL目前在國(guó)內(nèi)還不算流行棘利,可是在美利堅(jiān)已經(jīng)有不少巨頭在使用了:
GraphQL vs REST
我們來(lái)看一下對(duì)于不同API標(biāo)準(zhǔn)下橱野,從服務(wù)端獲取數(shù)據(jù)的區(qū)別。比如在REST API標(biāo)準(zhǔn)下善玫,有三個(gè)接口:
/users/
該接口返回某用戶(hù)基本信息
/users/posts
該接口返回某用戶(hù)所有的文章
/users/followers
該接口返回某用戶(hù)所有的關(guān)注者
如圖所示水援,要通過(guò)三個(gè)不同的請(qǐng)求才能獲得某用戶(hù)及其文章和關(guān)注者的信息,其中還存在很多不需要的信息茅郎。
再看一下GraphQL API的實(shí)現(xiàn):
客戶(hù)端聲明自己想要的信息蜗元,然后服務(wù)端根據(jù)請(qǐng)求返回相應(yīng)的數(shù)據(jù)
目前可見(jiàn)的優(yōu)點(diǎn):
避免了REST API中常見(jiàn)的信息過(guò)多或過(guò)少的問(wèn)題
信息過(guò)多是指,接口中總會(huì)存在客戶(hù)端不需要的信息系冗,信息過(guò)少是指單條接口無(wú)法滿(mǎn)足客戶(hù)端需求奕扣,需要請(qǐng)求多個(gè)接口才能滿(mǎn)足需要
前端可以快速迭代
在REST API中,一般都是后端定義好了API掌敬,返回固定的數(shù)據(jù)格式惯豆。當(dāng)前端業(yè)務(wù)或需求發(fā)生變化時(shí)池磁,后端很難跟上變動(dòng)的節(jié)奏。如今楷兽,業(yè)務(wù)變化已經(jīng)難以避免,所以當(dāng)前端和后端都要相應(yīng)地作出改動(dòng)芯杀,這樣效率勢(shì)必降低瘪匿。就我們公司業(yè)務(wù)來(lái)講寻馏,很多情況下,前端一兩天的改動(dòng)如果再拉上后端顽染,人多肯定要開(kāi)會(huì)再加上溝通成本的問(wèn)題粉寞,這個(gè)需求沒(méi)個(gè)一周兩周很難搞定唧垦。設(shè)想一下液样,如果在GraphQL標(biāo)準(zhǔn)下,除非大的改版坊秸,后端基本不用出人力來(lái)跟著一起需求評(píng)審褒搔,前端自己定義查詢(xún)的內(nèi)容就搞定了星瘾。
更深層次地進(jìn)行分析
當(dāng)客戶(hù)端可以選擇自己想請(qǐng)求數(shù)據(jù)的內(nèi)容時(shí)惧辈,這時(shí)候就可以分析出哪些信息是用戶(hù)感興趣的咬像,也可以更深層次地分析現(xiàn)有數(shù)據(jù)是如何被應(yīng)用的生宛。
此外陷舅,也可以分析出哪些信息用戶(hù)不再感興趣了莱睁。以上轉(zhuǎn)自騰訊云GraphQL簡(jiǎn)介
在GraphQL中芒澜,我們通過(guò)預(yù)先定義一張Schema和聲明一些Type來(lái)達(dá)到上面提及的效果痴晦,我們需要知道
- 對(duì)于數(shù)據(jù)模型的抽象是通過(guò)Type來(lái)描述的
- 對(duì)于接口獲取數(shù)據(jù)的邏輯是通過(guò)Schema來(lái)描述的
Type
對(duì)于數(shù)據(jù)模型的抽象是通過(guò)Type來(lái)描述的,每一個(gè)Type有若干Field組成部凑,每個(gè)Field又分別指向某個(gè)Type涂邀。
GraphQL的Type簡(jiǎn)單可以分為兩種比勉,一種叫做Scalar Type(標(biāo)量類(lèi)型)驹止,另一種叫做Object Type(對(duì)象類(lèi)型)。
Scalar Type
GraphQL中的內(nèi)建的標(biāo)量包含赡勘,String闸与、Int践樱、Float凸丸、Boolean、Enum瞭稼,對(duì)于熟悉編程語(yǔ)言的人來(lái)說(shuō),這些都應(yīng)該很好理解欲虚。
值得注意的是悔雹,GraphQL中可以通過(guò)Scalar聲明一個(gè)新的標(biāo)量腌零,比如:
prisma(一個(gè)使用GraphQL來(lái)抽象數(shù)據(jù)庫(kù)操作的庫(kù))中,還有DateTime和ID這兩個(gè)標(biāo)量分別代表日期格式和主鍵
在使用GraphQL實(shí)現(xiàn)文件上傳接口時(shí)益涧,需要聲明一個(gè)Upload標(biāo)量來(lái)代表要上傳的文件
總之,我們只需要記住饰躲,標(biāo)量是GraphQL類(lèi)型系統(tǒng)中最小的顆粒臼隔,關(guān)于它在GraphQL解析查詢(xún)結(jié)果時(shí),我們還會(huì)再提及它寄狼。
Object Type
僅有標(biāo)量是不夠的抽象一些復(fù)雜的數(shù)據(jù)模型的泊愧,這時(shí)候我們需要使用對(duì)象類(lèi)型
type Article {
id: ID
text: String
isPublished: Boolean
}
上面的代碼删咱,就聲明了一個(gè)Article類(lèi)型豪筝,它有3個(gè)Field,分別是ID類(lèi)型的id敲街,String類(lèi)型的text和Boolean類(lèi)型的isPublished多艇。
對(duì)于對(duì)象類(lèi)型的Field的聲明像吻,我們一般使用標(biāo)量复隆,但是我們也可以使用另外一個(gè)對(duì)象類(lèi)型昏名,比如如果我們?cè)俾暶饕粋€(gè)新的User類(lèi)型轻局,如下:
type Article {
id: ID
text: String
isPublished: Boolean
author: User
}
Article新增的author的Field是User類(lèi)型, 代表這篇文章的作者样刷。
總之置鼻,我們通過(guò)對(duì)象模型來(lái)構(gòu)建GraphQL中關(guān)于一個(gè)數(shù)據(jù)模型的形狀,同時(shí)還可以聲明各個(gè)模型之間的內(nèi)在關(guān)聯(lián)(一對(duì)多储藐、一對(duì)一或多對(duì)多)嘶是。
mongoose
- Schema: Mongoose 的一切始于 Schema辖源。每個(gè) schema 都會(huì)映射到一個(gè) MongoDB collection 克饶,并定義這個(gè)collection里的文檔的構(gòu)成誊辉。
定義一個(gè)schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let blogSchema = new Schema({
title: String,
author: String,
body: String,
comments: [{ body: String, date: Date }],
date: { type: Date, default: Date.now },
hidden: Boolean,
meta: {
votes: Number,
favs: Number
}
});
- Model: 基本文檔數(shù)據(jù)的父類(lèi),通過(guò)集成Schema定義的基本方法和屬性得到相關(guān)的內(nèi)容.
Models 是從Schema
編譯來(lái)的構(gòu)造函數(shù)堕澄。 它們的實(shí)例就代表著可以從數(shù)據(jù)庫(kù)保存和讀取的 documents。 從數(shù)據(jù)庫(kù)創(chuàng)建和讀取 document 的所有操作都是通過(guò) model 進(jìn)行的坞嘀。
var schema = new mongoose.Schema({ name: 'string', size: 'string' });
var Tank = mongoose.model('Tank', schema);
第一個(gè)參數(shù)是跟 model 對(duì)應(yīng)的集合( collection )名字的 單數(shù) 形式丽涩。 Mongoose 會(huì)自動(dòng)找到名稱(chēng)是 model 名字 復(fù)數(shù) 形式的 collection 。 對(duì)于上例继准,Tank 這個(gè) model 就對(duì)應(yīng)數(shù)據(jù)庫(kù)中 tanks 這個(gè) collection矮男。.model() 這個(gè)函數(shù)是對(duì) schema 做了拷貝(生成了 model)。 你要確保在調(diào)用 .model() 之前把所有需要的東西都加進(jìn) schema 里了崔泵!
- instance: 這就是實(shí)實(shí)在在的數(shù)據(jù)了. 通過(guò) new Model()初始化得到.