
介紹
GraphQL是一種查詢語言,是為 API 設計的資料查詢、修改的語言,主要使用於應用服務之間的溝通,尤其是前後端,類似於廣為人知的 Restful API。
GraphQL是一個由 Facebook 開發、而且公開的資料查詢語言。由 client 端定義好資料的格式,讓 server 端針對 client 的格式給予特定的資料。
這個做法給予了 client 端最大的彈性:client 端可以拉任何想要的資料。
優缺點
優點
- GraphQL 模式會在 GraphQL 應用中設置單一來源。它為企業提供了一種整合其整個 API 的方法。
- 一次往返通訊可以處理多個 GraphQL 調用。客戶端可得到自己所請求的內容,不會過度抓取。
- 嚴格定義的數據類型可減少客戶端與服務器之間的通信錯誤。
- GraphQL 具有自檢功能。客戶端可以請求一個可用數據類型的列表。這非常適合文檔的自動生成。
- GraphQL 允許應用 API 進行更新優化,而無需破壞現有查詢。
- GraphQL 不指定特定的應用架構。它能夠以現有的 REST API 為基礎,並與現有的 API 管理工具配合使用。
缺點
- GraphQL 請求的方式一律使用 POST,需要適應 Restful 的架構要再做對應調整
- 會將後端 API loading 轉移至前端
- 前端架構複雜度增加,維護的成本會同時提升,最終可能造成程式碼混淆
- 容易建構出效能不佳的代碼,GraphQL的最大問題是它在單個端點上運行
實作
npm init -y
npm install express express-graphql graphql nodemon
schema:定義查詢及類別
- 類別有Int、String、Boolean、float和ID,也可自己定義。
- [ ]代表陣列,[Int]代表整數陣列
- 在後方加入!代表不可為空
- 一般類別直接在類別後面加上!即可,範例如下
- schema 註解方法
- “””123″”” 三個”可用於多行註解
- “456” 單個”只用於單行
- #789 #註解後不會出現在 Graphql 顯示
resolvers:定義schema方法
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
//schema定義查詢及類別
const schema = buildSchema(`
type Query {
hello: String
user:String
age:Int!
}
`);
//定義查詢內容
const root = {
hello: () => {
return "Hello world!";
},
user: () => {
return "Hello";
},
age: () => {
return 15;
},
};
const app = express();
app.use(
"/graphql",
graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
})
);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () =>
console.log(`Now browse to http://localhost:${PORT}/graphql`)
);
到網址http://localhost:3000/graphql
在左側查詢輸入,即可看到資料
query{
hello
user
age
}
陣列定義方式
type Query {
arr(arrNum:Int!,arrNum1:Int):[Int]
//括號裡面為要帶入的參數
//後面的[]為要輸出的類別
}
陣列實作
const express = require("express");
const { graphqlHTTP } = require("express-graphql");
const { buildSchema } = require("graphql");
//getClassmate(classNo:Int!)表示定義的方法以及輸入的類型,分號後面為要回傳的資料
const schema = buildSchema(`
type Query {
getClassmates(classNo:Int!):[String]
}
`);
const root = {
getClassmates: (obj, args, context) => {
const arr = {
10:["大雄","胖虎","小夫"],
11:["大大雄","胖胖虎","小小夫"],
12:["靜香","小山","A夢"]
}
return arr[obj.classNo];
}
};
//obj內會存著帶入的參數
//args提供給 GraphQL 查詢中的字段的參數。
//context提供給每個解析器的值,並保存重要的上下文信息,例如當前登錄的用戶或對資料庫的訪問。
const app = express();
app.use(
"/graphql",
graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
})
);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () =>
console.log(`Now browse to http://localhost:${PORT}/graphql`)
);
到網頁左側輸入內容,可在右側看到資料
query{
getClassmates(classNo:11)
}
自定義類別
const express = require("express");
const { graphqlHTTP } = require("express-graphql");
const { buildSchema } = require("graphql");
const schema = buildSchema(`
"""自定義Account"""
type Account{
name:String
age:Int
sex:String
department:String
salary(city:String):Int
}
type Query {
account(username:String):Account
}
`);
const root = {
//直接對obj進行解構
account: ({ username }) => {
const name = username;
const age = 20;
const sex = "man";
const department = "冗員部";
//裡面的function參數也要記的解構
const salary = ({city}) => {
if (city === "新竹") {
return 100000;
} else if (city === "台北") {
return 80000;
} else {
return 50000;
}
};
return {
name,
age,
sex,
department,
salary,
};
},
};
const app = express();
app.use(
"/graphql",
graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
})
);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () =>
console.log(`Now browse to http://localhost:${PORT}/graphql`)
);
到網頁左側輸入內容,可在右側看到資料
#City的位置可自行替代參數,且輸入的參數如果是字串要用""包起來,無法使用''。
query{
account(username:"gary"){
name,
age,
sex,
department,
salary(city:"台北")
}
}
前端使用
需在目錄下新增 public/index.html ,內容如下,為按鈕及function
<button onclick="getData()">click</button>
<script>
const getData = () => {
//放置要拿的資料,這邊兩個$username需與後端帶入的參數使用相同變數名稱
const query = `
query Account($username:String){
account(username:$username){
name
age
department
sex
salary(city:"台北")
}
}
`;
//參數
const variables = { username: "han" };
fetch("/graphql", {
method: "POST",
headers: {
"content-type": "application/json",
"Accept": "application/json",
},
body: JSON.stringify({
query: query,
variables: variables,
}),
})
.then((res) => res.json())
.then((res) => console.log(res))
.catch((err) => console.log(err));
};
</script>
後端需新增此行程式碼,已接收及傳送對外資料
app.use(express.static('public'))
完成後打開網址 http://localhost:3000/index.html ,開啟開發者模式的console,按下按鈕即可拿到資料
Mutations
查詢使用query,有動到資料使用mutations,需特別注意,就算沒有用到query,也需要定義query不然會噴錯
- 要新增編輯等等需輸入的內容要使用input
const express = require("express");
const { graphqlHTTP } = require("express-graphql");
const { buildSchema } = require("graphql");
const cors = require("cors");
const schema = buildSchema(`
input AccountInput{
name:String
age:Int
sex:String
department:String
}
type Account{
name:String
age:Int
sex:String
department:String
}
type Mutation{
createAccount(input:AccountInput):Account
updateAccount(id:ID!,input:AccountInput):Account
}
type Query{
accounts:[Account]
}
`);
const fakeDB = {}
const root = {
accounts:()=>{
//因為無法直接回傳obj,因此要轉成array
let arr=[]
for(key in fakeDB){
arr.push(fakeDB[key])
}
return arr
},
createAccount:({input})=>{
fakeDB[input.name] = input
return fakeDB[input.name]
},
updateAccount:({id,input})=>{
const updateAccount =Object.assign({},fakeDB[id],input)
fakeDB[id] = updateAccount
return updateAccount
}
};
const app = express();
app.use(
"/graphql",
graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
})
);
app.use(cors());
app.use(express.static("public"));
const PORT = process.env.PORT || 3000;
app.listen(PORT, () =>
console.log(`Now browse to http://localhost:${PORT}/graphql`)
);
可在網頁那邊,左邊輸入以下內容新增資料
mutation{
#此部分為新增資料
createAccount(input:{
name:"Gary",
age:25,
sex:"男",
department:"冗員部"
}){
#此部分為回傳資料
name
age
sex
department
}}
可以新增1~2筆資料後使用下面語法查詢,需先將前面語法進行註解
query{
accounts{
name
age
department
sex
}
}
修改資料的部分,目前是使用名字當作id,因此使用Gary
mutation{
updateAccount(id:"Gary",input:{
age:18
}){
age
}
}
查看資料使用以下語法,也須將上方修改程式碼註解掉
query{
accounts{
name
age
department
sex
}
}