爲什麼要使用GraphQL
GraphQL是由Facebook團隊在2015年開源推出的一套用於替代傳統的REST API的框架。利用它能夠大幅提升開發效率以及應用性能。設想這樣一個場景:
你想要獲得一個用戶的朋友的國家信息,如果你使用的是REST API的話,你可能需要同時調用以下這些接口:
- /users/{userName}/friends: 返回類似
{id: 1, name: 'xxx', friends: [2,3,4] }
的結構 - /friends/{ID}: 返回類似
{id: 2, name: 'xxx', country: 23}
的結構 - /countries/{ID}: 返回類似
{id: 23, name: 'China', flag: 'xxx', language: 'Chinese'}
的信息
先取回以scq000
爲用戶名的朋友id,然後利用這些返回的用戶id再查詢詳細的信息,最後再拼裝成想要的數據結構。你會發現,隨着應用複雜度的提升,API的數量以及請求的數量成倍地增加,客戶端爲了想要獲得一個簡單的數據信息,要往返多次地調用不同的API。
而GraphQL的出現就是爲了解決上述這些問題的,它本質上是一門面向API的查詢語言(Query Language for API)。可以根據客戶端想要的數據,一次性地將數據取回來。
比如,上面的這個例子就能用下面的Graph 請求來描述:
{
user {
friend {
name
country
}
}
}
然後返回的數據是像這樣:
{
"user": {
"friend": {
"name": "scq000",
"country": "China"
}
}
}
同時目前GraphQL也支持各種語言,對於主流的編程語言都有良好地支持,生態環境也搭建地不錯。國外的大公司,如facebook, paypal, twitter, github等現在都在項目中大量使用GraphQL。
核心概念
GraphQL中一個核心的概念就是Schema, 它是客戶端和服務端之間進行溝通的協議,定義好對應的數據schema後,前後端就可以獨立地進行開發,從而提高效率。
一個schema是由query和mutation兩部分組成的,如:
schema {
query: Query,
mutation: Mutation
}
其中, query對應的就是請求數據的對象,而mutation主要負責進行副作用,如create, delete, update等操作。
types
一個GraphQL schema中最基本的組件是對象類型,可以用來表示能從服務器獲取什麼類型的對象。GraphQL內置的標量數據類型(Scalar Types)有以下幾種:
Int: a signed 32bit 帶符號的32位整數
Float: 有符號雙精度浮點型
String: UTF-8編碼的字符串
Boolean: 布爾型,true和false兩個值
ID: 唯一標識符,用以重新獲取對象或作爲緩存的鍵
還有枚舉類型enum:
enum Country {
CHINA
JAPAN
AMERICAN
}
默認情況下每一個內置類型的都可以被設置成null,如果需要確保某個字段不爲空,需要使用!
符號:
type Author {
id: ID!
firstName: string,
courses: [String]
}
注意到courses
的類型是[String]
,這表示這個字段是個數組類型。
除了使用內置類型外,還可以自定義類型:
type Query {
author_details: [Author]
}
type Mutation {
addAuthor(firstName: String, lastName: sring): Author
}
Queries
Fields
字段是Query對象上最基本的組成單位,服務端和客戶端的結構基本相同:
{
viewer {
name
}
}
Arguments
在GrahQL中,你可以給字段傳遞參數。這樣可以避免重複的API請求,如下所示:
{
followers (last: 3) {
nodes {
id
}
}
}
這表示請求最後3個記錄,你還可以使用:
{
author (id: "1000") {
name
age
}
}
這表示請求id爲1000的記錄。
alias
由於在請求中不能使用不同的參數來請求相同的字段,因此就需要使用alias來給字段起別名:
{
firstFollowers: followers (first: 3) {
nodes {
id
name
}
}
lastFollowers: followers (last: 3) {
nodes {
id
name
}
}
}
Fragments
片段(Fragment)是可複用的單位,可以讓你在多個Query對象中利用同一個Fragments:
{
nodes {
...userInfo
}
}
fragment userInfo on User {
id
bio
}
用fragment
關鍵字聲明一個片段對象,然後再使用的時候直接利用擴展運算符...
就可以了。
Operation name
我們一般在聲明查詢對象的時候都是用簡寫形式省略query
關鍵字和查詢名稱,不過如果需要減少代碼歧義,則需要顯式聲明:
query ViewerInfo {
viewer {
name
date
}
}
Variables
字段的參數可以是動態的,所以使用變量進行控制:
query ViewerInfo($isOwner: Boolean!) {
viewer {
id
name
start(ownedBy)
}
}
{
isOwner: false
}
Mutations
用來更新數據的(create, update, delete)的副作用。
Query的時候所有的查詢是同時執行的,但是Mutation操作是按順序執行的:
mutation NewStatus($input: ChangeUserStatusINput!) {
changeUserStatus(input: $input) {
clientMutationId
status {
message
}
}
}
生態以及工具
整個GraphQL架構是有客戶端和服務端兩部分組成中,其中客戶端主要負責處理數據請求的狀態管理,緩存,分頁,錯誤處理以及schema的校驗等工作。而服務端主要和數據庫打交道, 有schema 和resolvers, resolver主要處理業務邏輯。
現在比較主流的GraphQL客戶端框架有Apollo Client和Relay。
服務端可以使用Apollo Server, express graphql, graphql yoda等。
數據庫方面可以使用Prisma, 是替代傳統的ORM框架的方案,支持多種數據庫。
最後再推薦一些其他有用的工具:
https://graphql.org/swapi-graphql/
graphql voyager: 圖示,模型設計的工具
graphql faker: mock 數據
graphql visual editor: 可視化編輯器
如何使用
最後,我們來寫一個實際的demo來應用一下GraphQL:
首先我們要先來定義一下schema對象:
import {
GraphQLSchema,
GraphQLObjectType,
GraphQLInt
} from "graphql";
let counter = 1;
let schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: () => ({
counter: {
type: GraphQLInt,
resolve: () => counter
}
})
}),
mutation: new GraphQLObjectType({
name: "Mutation",
fields: () => ({
incrementCounter: {
type: GraphQLInt,
resolve: () => ++counter
}
})
})
})
這裏定義了一個counter
的字段,然後mutation裏有一個incrementCounter
的方法,每次執行這個mutation,都會讓counter
的值加1。
有了schema後,接着就可以利用express來開啓一個叫做/graphql
的接口,並使用已經定義好的schema:
const app = express();
const port = 3000
app.use('/graphql', GraphQLHTTP({
schema
}));
app.listen(port, () => console.log(`Example app listening on port ${port}`))
最後我們就可以直接在瀏覽器中輸入localhost:3000/graphql
使用了