摘要:如下圖嗯,如圖都已經查詢到我們保存的全部數據,并且全部返回前端了。如圖沒錯,什么都沒有就是查詢服務的界面。寫好了之后我們在配置一下路由,進入里面,加入下面幾行代碼。
GraphQL一種用為你 API 而生的查詢語言,2018已經到來,PWA還沒有大量投入生產應用之中就已經火起來了,GraphQL的應用或許也不會太遠了。前端的發展的最大一個特點就是變化快,有時候應對各種需求場景的變化,不得不去對接口開發很多版本或者修改。各種業務依賴強大的基礎數據平臺快速生長,如何高效地為各種業務提供數據支持,是所有人關心的問題。而且現在前端的解決方案是將視圖組件化,各個業務線既可以是組件的使用者,也可以是組件的生產者,如果能夠將其中通用的內容抽取出來提供給各個業務方反復使用,必然能夠節省寶貴的開發時間和開發人力。那么問題來了,前端通過組件實現了跨業務的復用,后端接口如何相應地提高開發效率呢?GraphQL,就是應對復雜場景的一種新思路。
官方解釋:
GraphQL 既是一種用于 API 的查詢語言也是一個滿足你數據查詢的運行時。 GraphQL 對你的 API 中的數據提供了一套易于理解的完整描述,使得客戶端能夠準確地獲得它需要的數據,而且沒有任何冗余,也讓 API 更容易地隨著時間推移而演進,還能用于構建強大的開發者工具。
下面介紹一下GraphQL的有哪些好處:
請求你所要的數據不多不少
獲取多個資源只用一個請求
自定義接口數據的字段
強大的開發者工具
API 演進無需劃分版本
本篇文章中將搭配koa實現一個GraphQL查詢的例子,逐步從簡單kao服務到mongodb的數據插入查詢再到GraphQL的使用,
讓大家快速看到:
搭建koa搭建一個后臺項目
后臺路由簡單處理方式
利用mongoose簡單操作mongodb
掌握GraphQL的入門姿勢
項目如下圖所示
1、搭建GraphQL工具查詢界面。
2、前端用jq發送ajax的使用方式
入門項目我們都已經是預覽過了,下面我們動手開發吧!??!
lets do it首先建立一個項目文件夾,然后在這個項目文件夾新建一個server.js(node服務)、config文件夾、mongodb文件夾、router文件夾、controllers文件夾以及public文件夾(這個主要放前端靜態數據展示頁面),好啦,項目的結構我們都已經建立好,下面在server.js文件夾里寫上
server.js
// 引入模塊 import Koa from "koa" import KoaStatic from "koa-static" import Router from "koa-router" import bodyParser from "koa-bodyparser" const app = new Koa() const router = new Router(); // 使用 bodyParser 和 KoaStatic 中間件 app.use(bodyParser()); app.use(KoaStatic(__dirname + "/public")); // 路由設置test router.get("/test", (ctx, next) => { ctx.body="test page" }); app .use(router.routes()) .use(router.allowedMethods()); app.listen(4000); console.log("graphQL server listen port: " + 4000)
在命令行npm install koa koa-static koa-router koa-bodyparser --save
安裝好上面幾個模塊,
然后運行node server.js,不出什么意外的話,你會發現報如下圖的一個error
原因是現在的node版本并沒有支持es6的模塊引入方式。
放心 我們用神器babel-polyfill轉譯一下就闊以了。詳細的請看阮一峰老師的這篇文章
下面在項目文件夾新建一個start.js,然后在里面寫上以下代碼:
start.js
require("babel-core/register")({ "presets": [ "stage-3", ["latest-node", { "target": "current" }] ] }) require("babel-polyfill") require("./server")
然后 在命令行,運行npm install babel-core babel-polyfill babel-preset-latest-node babel-preset-stage-3 --save-dev安裝幾個開發模塊。
安裝完畢之后,在命令行運行 node start.js,之后你的node服務安靜的運行起來了。用koa-router中間件做我們項目路由模塊的管理,后面會寫到router文件夾中統一管理。
打開瀏覽器,輸入localhost:4000/test,你就會發現訪問這個路由node服務會返回test page文字。如下圖
yeah~~kao服務器基本搭建好之后,下面就是,鏈接mongodb然后把數據存儲到mongodb數據庫里面啦。
實現mongodb的基本數據模型tip:這里我們需要mongodb存儲數據以及利用mongoose模塊操作mongodb數據庫
在mongodb文件夾新建一個index.js和 schema文件夾, 在 schema文件夾文件夾下面新建info.js和student.js。
在config文件夾下面建立一個index.js,這個文件主要是放一下配置代碼。
又一波文件建立好之后,先在config/index.js下寫上鏈接數據庫配置的代碼。
config/index.js
export default { dbPath: "mongodb://localhost/graphql" }
然后在mongodb/index.js下寫上鏈接數據庫的代碼。
mongodb/index.js
// 引入mongoose模塊 import mongoose from "mongoose" import config from "../config" // 同步引入 info model和 studen model require("./schema/info") require("./schema/student") // 鏈接mongodb export const database = () => { mongoose.set("debug", true) mongoose.connect(config.dbPath) mongoose.connection.on("disconnected", () => { mongoose.connect(config.dbPath) }) mongoose.connection.on("error", err => { console.error(err) }) mongoose.connection.on("open", async () => { console.log("Connected to MongoDB ", config.dbPath) }) }
上面我們我們代碼還加載了info.js和 studen.js這兩個分別是學生的附加信息和基本信息的數據模型,為什么會分成兩個信息表?原因是順便給大家介紹一下聯表查詢的基本方法(嘿嘿~~~)
下面我們分別完成這兩個數據模型
mongodb/schema/info.js
// 引入mongoose import mongoose from "mongoose" // const Schema = mongoose.Schema // 實例InfoSchema const InfoSchema = new Schema({ hobby: [String], height: String, weight: Number, meta: { createdAt: { type: Date, default: Date.now() }, updatedAt: { type: Date, default: Date.now() } } }) // 在保存數據之前跟新日期 InfoSchema.pre("save", function (next) { if (this.isNew) { this.meta.createdAt = this.meta.updatedAt = Date.now() } else { this.meta.updatedAt = Date.now() } next() }) // 建立Info數據模型 mongoose.model("Info", InfoSchema)
上面的代碼就是利用mongoose實現了學生的附加信息的數據模型,用同樣的方法我們實現了student數據模型
mongodb/schema/student.js
import mongoose from "mongoose" const Schema = mongoose.Schema const ObjectId = Schema.Types.ObjectId const StudentSchema = new Schema({ name: String, sex: String, age: Number, info: { type: ObjectId, ref: "Info" }, meta: { createdAt: { type: Date, default: Date.now() }, updatedAt: { type: Date, default: Date.now() } } }) StudentSchema.pre("save", function (next) { if (this.isNew) { this.meta.createdAt = this.meta.updatedAt = Date.now() } else { this.meta.updatedAt = Date.now() } next() }) mongoose.model("Student", StudentSchema)實現保存數據的控制器
數據模型都鏈接好之后,我們就添加一些存儲數據的方法,這些方法都寫在控制器里面。然后在controler里面新建info.js和student.js,這兩個文件分別對象,操作info和student數據的控制器,分開寫為了方便模塊化管理。
實現info數據信息的保存,順便把查詢也先寫上去,代碼很簡單
controlers/info.js
import mongoose from "mongoose" const Info = mongoose.model("Info") // 保存info信息 export const saveInfo = async (ctx, next) => { // 獲取請求的數據 const opts = ctx.request.body const info = new Info(opts) const saveInfo = await info.save() // 保存數據 console.log(saveInfo) // 簡單判斷一下 是否保存成功,然后返回給前端 if (saveInfo) { ctx.body = { success: true, info: saveInfo } } else { ctx.body = { success: false } } } // 獲取所有的info數據 export const fetchInfo = async (ctx, next) => { const infos = await Info.find({}) // 數據查詢 if (infos.length) { ctx.body = { success: true, info: infos } } else { ctx.body = { success: false } } }
上面的代碼,就是前端用post(路由下面一會在寫)請求過來的數據,然后保存到mongodb數據庫,在返回給前端保存成功與否的狀態。也簡單實現了一下,獲取全部附加信息的的一個方法。下面我們用同樣的道理實現studen數據的保存以及獲取。
實現studen數據的保存以及獲取
controllers/sdudent.js
import mongoose from "mongoose" const Student = mongoose.model("Student") // 保存學生數據的方法 export const saveStudent = async (ctx, next) => { // 獲取前端請求的數據 const opts = ctx.request.body const student = new Student(opts) const saveStudent = await student.save() // 保存數據 if (saveStudent) { ctx.body = { success: true, student: saveStudent } } else { ctx.body = { success: false } } } // 查詢所有學生的數據 export const fetchStudent = async (ctx, next) => { const students = await Student.find({}) if (students.length) { ctx.body = { success: true, student: students } } else { ctx.body = { success: false } } } // 查詢學生的數據以及附加數據 export const fetchStudentDetail = async (ctx, next) => { // 利用populate來查詢關聯info的數據 const students = await Student.find({}).populate({ path: "info", select: "hobby height weight" }).exec() if (students.length) { ctx.body = { success: true, student: students } } else { ctx.body = { success: false } } }實現路由,給前端提供API接口
數據模型和控制器在上面我們都已經是完成了,下面就利用koa-router路由中間件,來實現請求的接口。我們回到server.js,在上面添加一些代碼。如下
server.js
import Koa from "koa" import KoaStatic from "koa-static" import Router from "koa-router" import bodyParser from "koa-bodyparser" import {database} from "./mongodb" // 引入mongodb import {saveInfo, fetchInfo} from "./controllers/info" // 引入info controller import {saveStudent, fetchStudent, fetchStudentDetail} from "./controllers/student" // 引入 student controller database() // 鏈接數據庫并且初始化數據模型 const app = new Koa() const router = new Router(); app.use(bodyParser()); app.use(KoaStatic(__dirname + "/public")); router.get("/test", (ctx, next) => { ctx.body="test page" }); // 設置每一個路由對應的相對的控制器 router.post("/saveinfo", saveInfo) router.get("/info", fetchInfo) router.post("/savestudent", saveStudent) router.get("/student", fetchStudent) router.get("/studentDetail", fetchStudentDetail) app .use(router.routes()) .use(router.allowedMethods()); app.listen(4000); console.log("graphQL server listen port: " + 4000)
上面的代碼,就是做了,引入mongodb設置,info以及student控制器,然后鏈接數據庫,并且設置每一個設置每一個路由對應的我們定義的的控制器。
安裝一下mongoose模塊 npm install mongoose --save
然后在命令行運行node start,我們服務器運行之后,然后在給info和student添加一些數據。這里是通過postman的谷歌瀏覽器插件來請求的,如下圖所示
yeah~~~保存成功,繼續按照步驟多保存幾條,然后按照接口查詢一下。如下圖
嗯,如圖都已經查詢到我們保存的全部數據,并且全部返回前端了。不錯不錯。下面繼續保存學生數據。
tip: 學生數據保存的時候關聯了信息里面的數據哦。所以把id寫上去了。
同樣的一波操作,我們多保存學生幾條信息,然后查詢學生信息,如下圖所示。
好了 ,數據我們都已經保存好了,鋪墊也做了一大把了,下面讓我們真正的進入,GrapgQL查詢的騷操作吧~~~~
重構路由,配置GraphQL查詢界面別忘了,下面我們建立了一個router文件夾,這個文件夾就是統一管理我們路由的模塊,分離了路由個應用服務的模塊。在router文件夾新建一個index.js。并且改造一下server.js里面的路由全部復制到router/index.js。
順便在這個路由文件中加入,graphql-server-koa模塊,這是koa集成的graphql服務器模塊。graphql server是一個社區維護的開源graphql服務器,可以與所有的node.js http服務器框架一起工作:express,connect,hapi,koa和restify??梢渣c擊鏈接查看詳細知識點。
加入graphql-server-koa的路由文件代碼如下:
router/index.js
import { graphqlKoa, graphiqlKoa } from "graphql-server-koa" import {saveInfo, fetchInfo} from "../controllers/info" import {saveStudent, fetchStudent, fetchStudentDetail} from "../controllers/student" const router = require("koa-router")() router.post("/saveinfo", saveInfo) .get("/info", fetchInfo) .post("/savestudent", saveStudent) .get("/student", fetchStudent) .get("/studentDetail", fetchStudentDetail) .get("/graphiql", async (ctx, next) => { await graphiqlKoa({endpointURL: "/graphql"})(ctx, next) }) module.exports = router
之后把server.js的路由代碼去掉之后的的代碼如下:
server.js
import Koa from "koa" import KoaStatic from "koa-static" import Router from "koa-router" import bodyParser from "koa-bodyparser" import {database} from "./mongodb" database() const GraphqlRouter = require("./router") const app = new Koa() const router = new Router(); const port = 4000 app.use(bodyParser()); app.use(KoaStatic(__dirname + "/public")); router.use("", GraphqlRouter.routes()) app.use(router.routes()) .use(router.allowedMethods()); app.listen(port); console.log("GraphQL-demo server listen port: " + port)
恩,分離之后簡潔,明了了很多。然后我們在重新啟動node服務。在瀏覽器地址欄輸入http://localhost:4000/graphiql,就會得到下面這個界面。如圖:
沒錯,什么都沒有 就是GraphQL查詢服務的界面。下面我們把這個GraphQL查詢服務完善起來。
編寫GraphQL Schema看一下我們第一張圖,我們需要什么數據,在GraphQL查詢界面就編寫什么字段,就可以查詢到了,而后端需要定義好這些數據格式。這就需要我們定義好GraphQL Schema。
首先我們在根目錄新建一個graphql文件夾,這個文件夾用于存放管理graphql相關的js文件。然后在graphql文件夾新建一個schema.js。
這里我們用到graphql模塊,這個模塊就是用javascript參考實現graphql查詢。向需要詳細學習,請使勁戳鏈接。
我們先寫好info的查詢方法。然后其他都差不多滴。
graphql/schema.js
// 引入GraphQL各種方法類型 import { graphql, GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLID, GraphQLList, GraphQLNonNull, isOutputType } from "graphql"; import mongoose from "mongoose" const Info = mongoose.model("Info") // 引入Info模塊 // 定義日期時間 類型 const objType = new GraphQLObjectType({ name: "mete", fields: { createdAt: { type: GraphQLString }, updatedAt: { type: GraphQLString } } }) // 定義Info的數據類型 let InfoType = new GraphQLObjectType({ name: "Info", fields: { _id: { type: GraphQLID }, height: { type: GraphQLString }, weight: { type: GraphQLString }, hobby: { type: new GraphQLList(GraphQLString) }, meta: { type: objType } } }) // 批量查詢 const infos = { type: new GraphQLList(InfoType), args: {}, resolve (root, params, options) { return Info.find({}).exec() // 數據庫查詢 } } // 根據id查詢單條info數據 const info = { type: InfoType, // 傳進來的參數 args: { id: { name: "id", type: new GraphQLNonNull(GraphQLID) // 參數不為空 } }, resolve (root, params, options) { return Info.findOne({_id: params.id}).exec() // 查詢單條數據 } } // 導出GraphQLSchema模塊 export default new GraphQLSchema({ query: new GraphQLObjectType({ name: "Queries", fields: { infos, info } }) })
看代碼的時候建議從下往上看~~~~,上面代碼所說的就是,建立info和infos的GraphQLSchema,然后定義好數據格式,查詢到數據,或者根據參數查詢到單條數據,然后返回出去。
寫好了info schema之后 我們在配置一下路由,進入router/index.js里面,加入下面幾行代碼。
router/index.js
import { graphqlKoa, graphiqlKoa } from "graphql-server-koa" import {saveInfo, fetchInfo} from "../controllers/info" import {saveStudent, fetchStudent, fetchStudentDetail} from "../controllers/student" // 引入schema import schema from "../graphql/schema" const router = require("koa-router")() router.post("/saveinfo", saveInfo) .get("/info", fetchInfo) .post("/savestudent", saveStudent) .get("/student", fetchStudent) .get("/studentDetail", fetchStudentDetail) router.post("/graphql", async (ctx, next) => { await graphqlKoa({schema: schema})(ctx, next) // 使用schema }) .get("/graphql", async (ctx, next) => { await graphqlKoa({schema: schema})(ctx, next) // 使用schema }) .get("/graphiql", async (ctx, next) => { await graphiqlKoa({endpointURL: "/graphql"})(ctx, next) // 重定向到graphiql路由 }) module.exports = router
詳細請看注釋,然后被忘記安裝好npm install graphql-server-koa graphql --save這兩個模塊。安裝完畢之后,重新運行服務器的node start(你可以使用nodemon來啟動本地node服務,免得來回啟動。)
然后刷新http://localhost:4000/graphiql,你會發現右邊會有查詢文檔,在左邊寫上查詢方式,如下圖
重整Graphql代碼結構,完成所有數據查詢現在是我們把schema和type都寫到一個文件上面了去了,如果數據多了,字段多了變得特別不好維護以及review,所以我們就把定義type的和schema分離開來,說做就做。
在graphql文件夾新建info.js,studen.js,文件,先把info type 寫到info.js代碼如下
graphql/info.js
import { graphql, GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLID, GraphQLList, GraphQLNonNull, isOutputType } from "graphql"; import mongoose from "mongoose" const Info = mongoose.model("Info") const objType = new GraphQLObjectType({ name: "mete", fields: { createdAt: { type: GraphQLString }, updatedAt: { type: GraphQLString } } }) export let InfoType = new GraphQLObjectType({ name: "Info", fields: { _id: { type: GraphQLID }, height: { type: GraphQLString }, weight: { type: GraphQLString }, hobby: { type: new GraphQLList(GraphQLString) }, meta: { type: objType } } }) export const infos = { type: new GraphQLList(InfoType), args: {}, resolve (root, params, options) { return Info.find({}).exec() } } export const info = { type: InfoType, args: { id: { name: "id", type: new GraphQLNonNull(GraphQLID) } }, resolve (root, params, options) { return Info.findOne({ _id: params.id }).exec() } }
分離好info type 之后,一鼓作氣,我們順便把studen type 也完成一下,代碼如下,原理跟info type 都是相通的,
graphql/student.js
import { graphql, GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLID, GraphQLList, GraphQLNonNull, isOutputType, GraphQLInt } from "graphql"; import mongoose from "mongoose" import {InfoType} from "./info" const Student = mongoose.model("Student") let StudentType = new GraphQLObjectType({ name: "Student", fields: { _id: { type: GraphQLID }, name: { type: GraphQLString }, sex: { type: GraphQLString }, age: { type: GraphQLInt }, info: { type: InfoType } } }) export const student = { type: new GraphQLList(StudentType), args: {}, resolve (root, params, options) { return Student.find({}).populate({ path: "info", select: "hobby height weight" }).exec() } }
tips: 上面因為有了聯表查詢,所以引用了info.js
然后調整一下schema.js的代碼,如下:
import { GraphQLSchema, GraphQLObjectType } from "graphql"; // 引入 type import {info, infos} from "./info" import {student} from "./student" // 建立 schema export default new GraphQLSchema({ query: new GraphQLObjectType({ name: "Queries", fields: { infos, info, student } }) })
看到代碼是如此的清新脫俗,是不是深感欣慰。好了,graophql數據查詢都已經是大概比較完善了。
課程的數據大家可以自己寫一下,或者直接到我的github項目里面copy過來我就不一一重復的說了。
下面寫一下前端接口是怎么查詢的,然后讓數據返回瀏覽器展示到頁面的。
前端接口調用在public文件夾下面新建一個index.html,js文件夾,css文件夾,然后在js文件夾建立一個index.js, 在css文件夾建立一個index.css,代碼如下
public/index.html
GraphQL-demo GraphQL-前端demo
課程列表
- 暫無數據....
班級學生列表
- 暫無數據....
點擊常規獲取課程列表點擊常規獲取班級學生列表點擊graphQL一次獲取所有數據,問你怕不怕?
我們主要看js請求方式 代碼如下
window.onload = function () { $("#btn2").click(function() { $.ajax({ url: "/student", data: {}, success:function (res){ if (res.success) { renderStudent (res.data) } } }) }) $("#btn1").click(function() { $.ajax({ url: "/course", data: {}, success:function (res){ if (res.success) { renderCourse(res.data) } } }) }) function renderStudent (data) { var str = "" data.forEach(function(item) { str += "
css的代碼 我就不貼出來啦。大家可以去項目直接拿嘛。
所有東西都已經完成之后,重新啟動node服務,然后訪問,http://localhost:4000/就會看到如下界面。界面丑,沒什么設計美化細胞,求輕噴~~~~
操作點擊之后就會想第二張圖一樣了。
所有效果都出來了,本篇文章也就到此結束了。
附上項目地址: https://github.com/naihe138/GraphQL-demo
ps:喜歡的話丟一個小星星(star)給我嘛
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/90692.html
摘要:要對進行黑盒測試測試的最好辦法是對他們進行黑盒測試,黑盒測試是一種不關心應用內部結構和工作原理的測試方法,測試時系統任何部分都不應該被。此外,有了黑盒測試并不意味著不需要單元測試,針對的單元測試還是需要編寫的。 本文首發于之乎專欄前端周刊,全文共 6953 字,讀完需 8 分鐘,速度需 2 分鐘。翻譯自:RingStack 的文章 https://blog.risingstack.co...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
閱讀 3560·2021-09-22 10:52
閱讀 1588·2021-09-09 09:34
閱讀 1990·2021-09-09 09:33
閱讀 758·2019-08-30 15:54
閱讀 2596·2019-08-29 11:15
閱讀 713·2019-08-26 13:37
閱讀 1667·2019-08-26 12:11
閱讀 2975·2019-08-26 12:00