做者:方芳前端
閱讀時間大約10~15mingit
咱們常常說到一個術語叫API
或者說接口。在和服務端交互的時候,咱們會說要一個數據接口。在和客戶端交互的時候交互的時候,咱們會說要一個jsapi。API
的解釋是這樣的:API
是 Application Programming Interface
(應用程序編程接口)。顧名思義,它是一些預先定義的函數,目的是提供應用程序與開發人員基於某軟件或硬件得以訪問一組例程的能力,而又無需訪問源碼,或理解內部工做機制的細節。github
GraphQL
是Facebook
開發的API查詢語言,而不是一個數據庫。它爲咱們提供了一種更有效的設計、建立和使用 API的方法。從根本上說,它是 REST 的替代品。數據庫
GraphQL對前端很友好,使前端開發人員輕鬆,GraphQL也有一系列的前端庫(Apollo、Relay或Urql),前端能夠用如緩存、實時更新UI等功能。express
藉助GraphQL前端開發人員生產力能夠提升,產品開發速度加快,不管UI如何變後端不用變。編程
Facebook發佈GraphQL三年以來,社區也已經比較完善,也有不少公司在用。json
用遊戲列表來舉例說明一下GraphQL的使用。後端
首先咱們要準備的是服務端的遊戲列表的數據,須要注意的是,這個數據能夠來自數據庫、第三方、咱們的上游服務端等,GraphQL自身並非一個數據庫:api
[
{ id: 1, game_name: "風暴魔域(註冊送好禮)", short_name: "moyu", score: 4.2625498 },
{ id: 2, game_name: "侍魂朧月傳說", short_name: "shihun", score: 4.3800301 }
]
複製代碼
接下來須要介紹幾個GraphQL中常見的特性。緩存
GraphQL 有本身的語言類型,用於編寫模式。 這是一種可讀很高的模式語法,稱爲規範與描述語言(SDL)。不管使用何種技術,SDL 都是相同的,也就是說你能夠將其用於你想要的任何語言或框架。
這種模式語言很是有用,由於它更直觀的看出 API 具備哪些類型,可讀性很強。
使用graphql的第一步,咱們須要在graphql服務器編寫咱們本身的schema。
type Game {
id: ID!
game_name: String!
short_name: String!
score: Int
}
type Query {
games: [Game!]!
game(id: ID!): Game!
}
type Mutation {
createGame(id: ID!, game_name: String!, short_name: String!, score: Int): Game!
updateGame(id: ID!, game_name: String, short_name: String, score: Int): Game!
deleteGame(id: ID!): Game!
}
複製代碼
類型是 GraphQL 最重要的特性之一。類型是表示 API 外觀的自定義對象。例如,在遊戲大廳中,咱們須要有用戶、遊戲、禮包、評論等類型。
type Game {
id: ID!
game_name: String!
short_name: String!
score: Int
}
複製代碼
類型具備字段,這些字段返回特定類型的數據。 例如,咱們在上面建立的 Game 類型,咱們有一些 game_name,short_name 和 score 字段。 類型字段能夠是任何類型,並始終返回一種數據類型,如 Int,Float,String,Boolean,ID,對象類型列表或自定義對象類型。
其中!的意思是,這個字段不能爲空,因此上面的Game類型中,只有score是能夠爲空的。
query是GraphQL 中的獲取數據的方式。關於 GraphQL 中的查詢,最吸引人的地方之一就是你能夠得到所需的確切數據,很少很多。這個點我以爲真是的很是的棒,由於咱們如今在定義接口的時候有一個消耗在溝通上的點就是多方協調入參和參數返回。
type Query {
games: [Game!]!
game(id: ID!): Game!
}
複製代碼
在這裏咱們定義了兩個查詢,一個是查詢全部遊戲,一個是查詢指定id的遊戲。
在 GraphQL 中,更改是修改服務器上的數據並獲取更新數據的方式, 能夠認爲是咱們日常用的增刪改。
type Mutation {
createGame(id: ID!, game_name: String!, short_name: String!, score: Int): Game!
updateGame(id: ID!, game_name: String, short_name: String, score: Int): Game!
deleteGame(id: ID!): Game!
}
複製代碼
因爲實時通訊業務的需求,GraphQL如今也提供了subscription的功能。能夠實現服務端對客戶端的實時數據傳遞,意味着不管什麼時候在服務器中發生數據變化,而且每當調用該事件時,服務器都會將相應的數據發送到客戶端。經過訂閱,你可讓你的應用在不一樣的用戶之間保持更新。
基本的訂閱的schema是這樣寫的:
subscription {
games {
id
game_name
short_name
score
}
}
複製代碼
講完了graphql的幾個經常使用的特性以後,接下來要來描述一下,咱們在服務器端是怎麼告知graphql咱們須要的數據在哪裏的。
GraphQL社區提供了一個支持http的中間件,express-graphql,藉助此能夠很簡單實現一個支持graphql的服務器,由於本次不主要介紹服務器的搭建,有興趣能夠查閱github。GraphQL的發明最初是爲了提高前端的開發效率,這篇文章所講只描述在前端中的使用,可是graphql是能夠在其餘語言中使用的,若是你們有興趣也能夠自行查閱。
在服務器端須要有一個resolvers的js文件,做用是用來告知graphql從哪裏去取數據,而且描述對數據的處理。
import { games } from "./db";
const resolvers = {
Query: {
game: (parent, { id }, context, info) => {
return games.find(user => game.id == id);
},
games: (parent, args, context, info) => {
return games;
}
},
Mutation: {
createGame: (parent, { id, game_name, short_name, score }, context, info) => {
const newGame = { id, game_name, short_name, score };
games.push(newGame);
return newGame;
},
updateGame: (parent, { id, game_name, short_name, score }, context, info) => {
let newGame = games.find(game => game.id === id);
newGame.game_name = game_name;
newGame.short_name = short_name;
newGame.score = score;
return newGame;
},
deleteGame: (parent, { id }, context, info) => {
const gameIndex = games.findIndex(game => game.id === id);
if (gameIndex === -1) throw new Error("Game not found.");
const deletedGames = games.splice(gameIndex, 1);
return deletedGames[0];
}
}
};
export default resolvers;
複製代碼
以上是對應咱們上面的schema的一份完整的resolver文件。
上面講的都是服務器端的設置與操做,那客戶端應該以什麼樣的規則來與服務端交互。
以請求遊戲列表爲例:
query {
games {
id
game_name
short_name
score
}
}
複製代碼
若是要請求某個遊戲:
query {
game(id: 1) {
id
game_name
short_name
score
}
}
複製代碼
若是須要建立一個遊戲
mutation {
createGame(id: 3, game_name: "王者榮耀", short_name: "yxzj", score: 4.7686622) {
id
game_name
short_name
score
}
}
複製代碼
上面咱們說了客戶端和服務端分別該怎麼作,可是先後端的交互仍是不可避免的要經過http協議來傳遞數據。那在GraphQL下,咱們是如何操做的?
首先,仍是要保證咱們的GraphQL服務器支持http。
以查詢舉例,咱們在服務端須要接受到的是一個query信息,以下面所示:
query {
games {
game_name
}
}
複製代碼
咱們該怎麼把這些數據傳遞給服務端?
在get方式下:
http://myapi/graphql?query={games{game_name}}
複製代碼
查詢變量能夠做爲 JSON 編碼的字符串發送到名爲 variables 的附加查詢參數中。
若是須要傳遞的參數不少,建議使用post方式傳遞數據。
當前來講,和服務端交互,咱們經常使用的是RESTful API。REST(representational state transfer表象性狀態轉變)架構指的是URI定位資源,用HTTP動詞(GET,POST,DELETE,DETC)描述操做,用HTTP Status Code傳遞Server的狀態信息。REST的主要原則有幾個:網絡上的全部事物都被抽象爲資源,每一個資源都有一個惟一的資源標識符,同一個資源具備多種表現形式(xml,json等),對資源的各類操做不會改變資源標識符,全部的操做都是無狀態的。
好比,仍是說咱們的games接口,對於「遊戲」咱們有增刪改查四種操做,怎麼定義RESTful的接口?
增長一個遊戲,uri: n.ssp.qq.com/api/game 接口類型:POST
刪除一個遊戲,uri: n.ssp.qq.com/api/game 接口類型:DELETE
修改一個遊戲,uri: n.ssp.qq.com/api/game 接口類型:PUT
查找一個遊戲,uri: n.ssp.qq.com/api/game 接口類型:GET
由上面兩個圖片的比較,咱們能夠發現,restful api是多入口的,每個請求服務端都有特定的返回,若是客戶端須要一些新的數據,必定會涉及服務端的修改。graphql的請求是單一入口的,服務端設置好schema以後,能夠根據客戶端傳入的請求信息可預知的返回客戶端須要的信息,而不涉及服務端的修改。