Tec ‧ 士多開發日記系列:第一篇:GraphQL簡介
2021-05-21
Tec‧士多
話說Tecky入面有間「Tec。士多」,大家攞完零食飲品,放低錢入錢箱就可以了。因為小弟最近研究緊GraphQL,所以小弟就諗可唔可以用GraphQL開發一個網上版「Tec。士多」。點知。。。因為是設計過程發現可以加入不同元素入去做示範,例如,Ramda,Prisma, Figma(https://www.figma.com/), storyboard, PostCSS, JS-in-CSS,CSS Variables, Tailwind CSS, PWA, Firebase ,MongoDB, AWS lambda, etc..., 所以小弟打算寫一系列文章透過開發「Tec。士多」示範各種工具。(希望唔會吹大左嘅 ,爛尾🙈。)
好!故事開始。。。
REST是唔是一種完美既溝通形式?
注意:假設大家對REST有基本概念,我不在此詳述·
無人完美。是Internet世界當然都無完美既溝通形式。REST都會遇到樽頸位。舉個例子,once upon a time,小弟去北海道旅行。當年北海道仍未有4G服務,但是我買錯左一張4G SIM去當地上網。結果只能到用到3G服務。開公司email portal(想當年,小弟非常勤力,心繫公司架😅)非常慢,用FB、WS都不太暢順。原因是。。。
-
整個web services/web site既設計只是針對相對較大螢幕裝置,例如,Laptop等。螢幕大,可以顯示比較多資訊。隨著手機普及,手機已經取代好多Laptop/Desktop工作同娛樂。因為手機螢幕相對地細,所以用家要看在細螢幕看大量資訊非常困難。
-
首先,作為一個developers問自己一句,用家是唔是需要是手機上"同一頁,同一時間"看到咁多資訊呢?
-
如果唔需要,傳輸太多不必要既資訊,由於浪費手機網絡頻寬(Bandwidth),所以未能有效率運用Bandwidth。呢種情況又稱為過度獲取/數據冗餘(over-fetching), 或者,
-
當然可以拆開幾個web services嚟解決問題。如果我今次需要攞完整資訊 ,就會造成過度請求/請求冗餘(under-fetching)。
-
如果要,就要有效運用Bandwidth,以最快速度下載。因為大量資訊是低頻寬下進行傳輸(一次過),所以手機下載需時。結果影響用家體驗 (User Experiences)。
-
即是點呀?需要分批同選擇性傳輸資訊。Developers們聽到呢個位開始頭痛了,
-
Developers需要設計另一套API專門俾手機呢類相對較細螢幕裝置了。Developers們聽到呢到更加頭痛,
-
萬一有一個核心功能需要大更新,例如,每封Email自動加返同事Signature,Developers 需要更新兩套API。工作量大左,出錯機會多左。瞓覺既時間少左,打機既時間少左。
有無REST以外的選擇呢?
有!GraphQL是其中一解決方法。GraphQL既好處:
- 使用**類似SQL Statement的語法(GraphQL Query Statement)**由sever取得資料。由Client決定需要的資料好處是可以是一次HTTP Request取得所需資料,可以避免over-fetching;又可以減少Request次數,提升下載速度。
- 有效減少backend同frontend耦合性(Coupling)。因為Frontend使用類似SQL Statement語法向Backend取得所需資料,所以Frontend只需用類似SQL Statement形式明確指出需要的資料就可以了。Backend的角色只是整合(Consolidating/Summarizing/Collecting)Client是使用整個應用程式(所有流程)需要的資料供Frontend備用。呢個GraphQL的特色又稱為單一數據源(Single Source of Truth)。
- 支援多種編程語言。因為GraphQL是獨立於任何編程話言。GraphQL本身只是一份規範文件。好多有心人已經開發出不同語言版本。
- 容易擴展(Scalability)。隨藉微服務(Microservices)盛行,是REST下部署微服務面對一定的難度同挑戰。GraphQL就能夠提供一個彈性介面容易部署微服務。
- GraphQL提供一個強類型的查詢語言可以每令編程減小出錯。強類型是什麼?是我地既課程會詳細介紹。
- **不需在多個版本共存進下更新。**因為GraphQL可以通知用户個別資訊將會被deprecated(廢棄),所以經過一段時日後就可以從Backend移除相關功能就可以了。
聽落好抽象?即是點呀?看埋個以下簡介再返轉頭看會明白多D。
GraphQL概念入門
注意:假設大家已經知道如何建立Express.js Server,在此不再詳述。
GraphQL Server會以Express.js Server為基礎,所以GraphQL模組(module)是會以中介軟體(middleware)形式放入Express.js Server。簡單講,GraphQL會以一個插件形式夾是Frontend同Backend中間的一個代理人,佢負責解讀同傳遞/交換資訊。
GraphQL Middleware會根據Frontend提供的GraphQL Query Statement@HTTP Request走去不同微服務(Microservices) / 資料庫(Database)/第三方服務提供者(Third-Party Service Providers)取得所需資訊再傳回Frontend。
GraphQL Query Statement是什麼?簡單講,Frontend會以物件為本(Object Orientation)方式要求Backend提供相關物件數據。用讀取個人資料Web Service做例子示範:
//app.ts (Backend) //person.ts class Person{ private name:string; private age: number; private email: string; constructor(name:string,age:number,email:string){ this.name = name; this.age = age; this.email = email; } } //Express.js Server //Purpose: get all of person names app.get("/personName",(req:Request,res:Response)=>{ const personList:Person[] = []; personList.push(new Person("Tom",34,"tom@gmail.com")); personList.push(new Person("Peter",30,"peter@gmail.com")); personList.push(new Person("Ken",25,"ken@gmail.com")); res.json(personList); })
如果Frontend要request所有Person資料寫法如下:
//index.js const res = await fetch("/personName"); const people = await res.json(); //[{"Tom",34,"tom@gmail.com"},{"Peter",30,"peter@gmail.com"},{"Ken",25,"ken@gmail.com"}]
以上既web service既原本目的只是攞Person Name,但是連同其他資料(例如,age,email)一拼攞返嚟。造成浪費Bandwidth。當然可以拆開幾個web services嚟解決問題(如下例子),但是如果我需要攞一個完整Person List(包括name,age,email)就會造成過度請求/請求冗餘(under-fetching)。因為起太多HTTP requests,所以造成應用程式反應過慢。
const personList:Person[] = []; personList.push(new Person("Tom",34,"tom@gmail.com")); personList.push(new Person("Peter",30,"peter@gmail.com")); personList.push(new Person("Ken",25,"ken@gmail.com")); app.get("/personName",(req:Request,res:Response)=>{ res.json(personList.map(person=>person.name)); }); app.get("/personAge",(req:Request,res:Response)=>{ res.json(personList.map(person=>person.age)); }); app.get("/personEmail",(req:Request,res:Response)=>{ res.json(personList.map(person=>person.email)); });
如何用GraphQL解決以上問題?要改以上既Frontend Code由"REST"改成"GraphQL"寫法(GraphQL Server暫時當佢一個black box先,小弟會是一下個篇文章詳細介紹)。
const query = " { person{ name } } " const body = { query //qurery:"{person:{name}}" } const queryRes = await fetch("/graphql"/*<-graphql server url*/,{ method: "POST", headers:{ "Content-Type":"application/json" }, body:JSON.stringify(body) }); const personNameList = await queryRes.json();
發生咩事😵?我看緊咩?小弟逐步拆解佢。首先,const query入面既string是咩意思?
{ person{ name } }
回應"GraphQL Query Statment是什麼?"。SQL Statement可以是Select入面選擇讀取咩column。GraphQL當中,Frontend可以用GraphQL Query Statement(即是以上既body內容 = SQL Select Statement) 要求需要的資訊。GraphQL Query Statement會以物件為本的方式要求Backend提供相關物件數據。以上既例子為例,Frontend攞所有person入面name的數據。
如果今次我想攞Person List(包括name,age,email),我只需改一改GraphQL Query Statement就可以。GraphQL Backend不需改任何code(不再需要create 3 個web services,不再擔心over-fetching/under-fetching),只是改動Frontend
之GraphQL Query Statement fetch去同一個web service就可得到不同資料。
{ person{ name age email } }
大家開始感受到GraphQL既好處未?
const body = { query //qurery:"{person:{name}}" } const queryRes = await fetch("/graphql"/*<-graphql server url*/,{ method: "POST", headers:{ "Content-Type":"application/json" }, body:JSON.stringify(body) }); const personNameList = await queryRes.json(); /*personNameList = { "data": { person:[ { "name": "Tom" }, { "name": "Peter" }, { "name": "Ken" }, ] } } */
當砌好GraphQL Query Statement後,只要用fetch傳送GraphQL Query Statement去GraphQL Server就會傳回Frontend指定資訊。大家細心觀察就會發現。。。。其實grahpql既結果會以JSON形式回傳,整個流程同REST無異。
{ "data": { person:[ { "name": "Tom" }, { "name": "Peter" }, { "name": "Ken" }, ] } }
{ "data": { person:[ { "name": "Tom", "age": 34, "email":"tom@gamil.com" }, { "name": "Peter", "age": 30, "email": "peter@gmail.com" }, { "name": "Ken", "age": 25, "email":"ken@gmail.com" }, ] } }
理論部分聽到好悶?我地開始實作環節了🥳!
GraphQL Server 設定
大家可以自己動手起左 Express.js Server先,不在此詳述。
安裝 GraphQL模組,請在VS Code下的Terminal打:
yarn add graphql @types/graphql apollo-server
坊間有好多第三方GraphQL模組,以Apollo GraphQL最為人熟悉。Apollo GraphQL提供好多方便工具快速開發GraphQL Server同測試。
補充: 其實可以用GraphQL 官方模組開發Server同測試,但是開發時間比較長。小弟自己玩既時候都是直接GraphQL 官方模組開發。因為想左俾大家容易上手,所以選擇Apollo GraphQL做示範。此外用GraphQL 官方模組既好處是開發某D功能彈性比較大,例如,Payment,Login等。。。
GraphQL Server 實作
是Express.js入面既entry point file,例如,app.ts入以下既code:
//直接import graphql server class,instance之後直接起動。 //typeDefs,resolvers<-- 會是下一篇文章詳情講解 import {ApolloServer,gql} from 'apollo-server-express'; import Express from 'express'; class Person{ public name:string; public age: number; public email: string; constructor(name:string,age:number,email:string){ this.name = name; this.age = age; this.email = email; } } const personList:Person[] = []; personList.push(new Person("Tom",34,"tom@gmail.com")); personList.push(new Person("Peter",30,"peter@gmail.com")); personList.push(new Person("Ken",25,"ken@gmail.com")); const app = Express(); const typeDefs = gql` type Person{ name:String, age:Int, email:String } type Query{ person:[Person] } `; const resolvers= { Query:{person:()=>personList}, Person:{ name:(parent:Person)=>parent.name, age:(parent:Person)=>parent.age, email:(parent:Person)=>parent.email } }; const server = new ApolloServer({typeDefs,resolvers}); server.applyMiddleware({app,path:'/graphql'}); //<-- 記得是"GraphQL概念入門"開頭提過Middleware既概念嗎? app.listen(4000,()=>{ console.log("GraphQL Server is started at http://localhost:4000/graphql"); });
可以起動台Server了🥳。
node .
GraphQL Server 測試
GraphQL模組提供一個測試平台俾Developers做測試。如果是Apollo GraphQL,測試平台叫GraphQL Playground。如果GraphQL 官方模組,測試平台叫iGraphQL。是Browser打以下URL:
http://localhost:4000/graphql
是平台左邊Panel打:
{
person{
name
}
}
👆🏻還記得佢嗎?
是右邊Panel會出現結果。
打開右邊Panel可以看到呢台GraphQL Server可以提供的數據。
下一篇文章詳細介紹GraphQL Server 實作方法。
References
留言
閱讀更多
常見的 Bootstrap 新手中伏位:從手帶 app (居安抗疫) 下載頁一齊睇
2020-03-26
筆者一看到居安抗疫這個網站就覺得排位怪怪的,打開後發現,充滿著常見的 Bootstrap 新手中伏位!讓我們一起來看一看!
零基礎.10分鐘輕鬆製作STEM教材
2020-07-03
因為疫情學校停課,造就「網上教學」興起。但是教師們多數只是用視訊會議軟體進行網上「授課」,但是並不是真正既「網上教學」。根據範式轉移:網上教學的迷思,eLearning(網上教學)應該是:
0成本!分析「保就業計劃」數據,超方便隨時網上更新分享分析結果
2020-08-20
根據工貿署2020年3月數字,中小企是佔本港商業單位總數98%以上,但是從「保就業」數據當中得知,中小企只是佔13.6%。可以等第二期結果出爐再統計一下。從數據分析得知,就算派得最多錢唔代表可保就業。成功申請的中小企比較小;大部分資助落入0人企業和微型企業手上,造成分配不公。成效成疑。同時從中反映出成個計畫審批唔透明,無完善監管機制阻止濫用。政府需要好好檢討完善成個計畫。
分析「保就業計劃」數據,超方便隨時網上更新分享分析結果 (Microsoft Power BI 版本)
2020-09-02
根據工貿署2020年3月數字,中小企是佔本港商業單位總數98%以上,但是從「保就業」數據當中得知,中小企只是佔13.6%。可以等第二期結果出爐再統計一下。從數據分析得知,就算派得最多錢唔代表可保就業。成功申請的中小企比較小;大部分資助落入0人企業和微型企業手上,造成分配不公。成效成疑。同時從中反映出成個計畫審批唔透明,無完善監管機制阻止濫用。政府需要好好檢討完善成個計畫。
Tec ‧ 士多開發日記系列:第二篇:實作GraphQL Server(基礎入門)- Part 1
2021-06-21
上一編Blog已經詳細講解GraphQL Frontend既實作方法。今編Blog就深入講解GraphQL Server運作模式及如何實作。開始講解GraphQL Server運作原理及實作之前,有三個概念一定要了解左先,模式(Schema)、解析器(Resolver)及資訊源(Data Sources)。