GraphQL.js 與服務端互動的新方式

winyh發表於2019-05-26

GraphQL 官方解釋為:一種用於 API 的查詢語言

(文章不斷完善中...我是寫一點更新下)

前面有寫過一篇文章《Next.js服務端渲染框架實戰》中有提到會把資料請求與後端互動的方式從restful API方式切換成GraphQL方式。這篇文章將會嘗試用GraphQL的方式與後端進行資料互動,先立個Flag,寫個本地demo,實現基本的CURD(資料的增刪改查)操作,歡迎大家一起交流討論。

為啥要嘗試GraphQL

從入門做前端以來,前臺的資料展示由最初的混合開發模式,後端模板渲染靜態頁面,同時後端將從資料查詢到的資料變數嵌入模板,實現了前端的資料展示。後來演變到前後端分離了,前端通過非同步請求,restful API的方式請求後臺介面拿到資料然後渲染到頁面。頁面的一些增刪改查操作也是通過API介面的方式跟後端做互動。那問題就來了,日常開發的時候並不能保證提前定下的API介面欄位百分百的準確,這就涉及到後續較大可能性要修改介面欄位了。還有更奇葩的後端提供的介面是這樣的。丟擲一個介面,包含的資料層級特別深,你問他我要的欄位在哪,回答是反正你要的欄位在裡面,自己找去。。。後臺仁兄提前樂呵呵的下班了,你加班找來找去,頓時心中一萬個XXX.當然如果後來越來樾規範,就不存在這種噁心的介面了。但介面欄位變動是不太可能消除的,那有沒有什麼方法可以自己修改欄位名稱,修改資料結構,拿到自己想要的資料?哈哈,自己寫後端介面算一個解決辦法。當然還有更好的解決辦法,就是今天的主角GraphQL了。

GraphQL優勢

  • 請求你所要的資料。查詢返回的資料是可預測的,因為控制資料的是應用(前端)而不是服務端
  • 獲取多個資源,只用一個請求。自定義資料層次巢狀,減少資料冗餘和太多的介面。
  • GraphQL 對你的 API 中的資料提供了一套易於理解的完整描述。可以很好的組織後端放回到前臺的資料結構

實踐是檢驗真理的唯一標準

題外話:我自學的程式設計技術,野生程式設計師,剛入門的時候總是迷茫不知所措。看文件N遍還是不知所以然,後來嘗試這按照教程去寫程式碼,剛開始自己都知道寫的是什麼意思,但是寫的過程中會遇到很多文件裡沒有提到的東西。逼迫著你去百度找到解決辦法,那個時候也沒人指導,身邊頁也沒有搞程式開發的。自學起來真的很痛苦,後來發現學程式設計文件要看。更重要的是要實踐,實際的寫程式碼,會發現掌握的速度遠比看文件要快。親身體會建議大家,想學習更透徹,直接寫程式碼吧。當然現在很多時候真的可以做到看文件就可以學會新技術了,那是因為有這幾年的技術積累,不管是計算機概念還是原理都比剛開始入門時一窮二白時的基礎好太多了,當你沉澱下來的時候你會發現,其實自己的知識體系超過了很多科班出生的程式設計師。最後:想深入理解技術原理,直接寫程式碼吧!

正式開始: 技術棧很簡單:Express+GraphQL

這裡會展示一個基於 Express web伺服器的一個 GraphQL API 服務端參考實現.首先新建一個資料夾express,在當前資料夾執行

npm install express express-graphql graphql faker --save 
複製程式碼

在express 資料夾下新建 server.js檔案,檔案內容先丟出來,然後逐句解釋說明

var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');

var faker = require('faker');

var randomName = faker.name.findName(); // Rowan Nikolaus
var randomEmail = faker.internet.email(); // Kassandra.Haley@erich.biz
var randomCard = faker.helpers.createCard(); // random contact card containing many properties

var {
    graphql,
    GraphQLSchema,
    GraphQLObjectType,
    GraphQLString,
    GraphQLInt,
    GraphQLList,
    GraphQLFloat,
    GraphQLID
}  = require('graphql');

const articleType = new GraphQLObjectType({
    name: 'Citys',
    fields: {
      time: { type: GraphQLString },
      title: { type: GraphQLString },
      order: { type: GraphQLInt}
    }
})
  
  
var Articleschema = new GraphQLSchema({
    query: new GraphQLObjectType({
      name: 'RootQueryType',
      fields: {
        id: {
            type: GraphQLID,
            description: '唯一id',
            args: {
                id: {  // 這裡定義引數,包括引數型別和預設值
                    type: GraphQLID,
                    defaultValue: 1
                }
            },
            resolve : (_, args) => {
                console.log(_, args.id)
                return Number(1 + args.id)
            }
        },
        hashed:{
            type: GraphQLFloat,
            description: '隨機值',
            resolve : () => {
                return Math.random()
            }
        },
        name: {
            type: GraphQLString,
            description: '姓名',
            resolve : () => {
                return randomName
            }
        },
        hello: {
          type: new GraphQLList(articleType),
          resolve() {
            return  [
                {'time':'2019-2-1', 'title':'武漢', 'order':1}, 
                {'time':'2019-2-2', 'title':'南昌', 'order':2},
                {'time':'2019-2-3', 'title':'貴陽', 'order':3},
            ]
          }
        },
        age: {
            type: GraphQLString,
            resolve: () => 18
        },
        list: {
            type: new GraphQLList(articleType),
            description: '城市資訊陣列',
            resolve: async (_, args) => {
                return await [
                    {'time':'2019-2-1', 'title':'武漢', 'order':1}, 
                    {'time':'2019-2-2', 'title':'南昌', 'order':2},
                    {'time':'2019-2-3', 'title':'貴陽', 'order':3},
                ]
            }
        },
      }
    })
});


var Commentschema = new GraphQLSchema({
    query: new GraphQLObjectType({
      name: 'RootQueryType',
      fields: {
        id: {
            type: GraphQLID,
            description: '唯一id',
            args: {
                id: {  // 這裡定義引數,包括引數型別和預設值
                    type: GraphQLID,
                    defaultValue: 1
                }
            },
            resolve : (_, args) => {
                console.log(_, args.id)
                return Number(1 + args.id)
            }
        },
        hashed:{
            type: GraphQLFloat,
            description: '隨機值',
            resolve : () => {
                return Math.random()
            }
        },
        name: {
            type: GraphQLString,
            description: '姓名',
            resolve : () => {
                return randomName
            }
        },
        hello: {
          type: new GraphQLList(articleType),
          resolve() {
            return  [
                {'time':'2019-2-1', 'title':'武漢', 'order':1}, 
                {'time':'2019-2-2', 'title':'南昌', 'order':2},
                {'time':'2019-2-3', 'title':'貴陽', 'order':3},
            ]
          }
        },
        age: {
            type: GraphQLString,
            resolve: () => 18
        },
        list: {
            type: new GraphQLList(articleType),
            description: '城市資訊陣列',
            resolve: async (_, args) => {
                return await [
                    {'time':'2019-2-1', 'title':'武漢', 'order':4}, 
                    {'time':'2019-2-2', 'title':'南昌', 'order':5},
                    {'time':'2019-2-3', 'title':'貴陽', 'order':6},
                ]
            }
        },
      }
    })
});

var app = express();

app.get('/', (req, res) => res.send('Welcome Graphql!'))

app.use('/graphql', graphqlHTTP({
  schema: Articleschema,
  graphiql: true,
}));


app.use('/comment', graphqlHTTP({
    schema: Commentschema,
    graphiql: true
}))

app.listen(5000, () => console.log('Now browse to localhost:4000/graphql'));

// # 每個欄位都有對應的執行函式,也就是resolver函式
複製程式碼

接下來修改package.json 加入npm 執行指令碼。這裡的nodemon 是node的工具,你也可以直接用node server.js.但是無法自動監聽 server.js 檔案修改後自動重啟。nodemon 可以實現server.js 檔案修改後自動重啟服務。

{
  "scripts": {
    "dev":"nodemon server.js"
  },
  "dependencies": {
    "express": "^4.16.4",
    "express-graphql": "^0.7.1",
    "graphql": "^14.1.1"
  },
  "devDependencies": {
    "faker": "^4.1.0"
  }
}

複製程式碼

相關文章