Visual Studio Code开发基于Nodejs的REST和GraphQL 实战

优势:按需请求,不多不少!

一、环境配置

1.安装Visual Studio Code的最新版本

2.安装MongoDB的最新Windows或Linux版本承载数据,把数据导入graphql数据库中,集合名称为:base.diseases,是国家标准疾病库字典,本文演示数据只有很少的三个键值对:code,name,assistant,如果你有1024个,则慢慢加吧!不过我有个工具可以批量秒生成!

3.安装NodeJS的最新版本用于承载项目。

4.升级npm到最新版本,执行:npm i -g npm

5.找个美好的地方创建graphql-demo目录,环境配置完毕。

二、初始化项目

1.打开Visual Studio Code开发工具,并立即打开刚刚创建好的graphql-demo目录。

2.在菜单“查看”中单击“终端”,或者按Ctrl+`,就是波浪线键上靠下边的那个反引号,打开终端,执行:npm init -y,会出现package.js文件,说明创建成功,注意观察一下此文件的内容,具体内容请查阅网络资料吧,这里不再阐述。

3.安装应用程序相关的必须依赖包(注意用空隔隔开):npm i nodemon mongoose express express-graphql graphql body-parser,安装完毕后,在package.js中会有如下内容:

4.修改package.js的配置,把

"scripts":

{

    "test": "echo \"Error: no test specified\" && exit 1"

  }

改为

"scripts":

{

"test": "echo \"Error: no test specified\" && exit 1",

"serve": "nodemon server.js",

  }

5.在graphql-demo目录下,新建主程序文件:server.js,内容如下,你直接拷贝一下即可。

const mongoose = require('mongoose')

mongoose

.connect('mongodb://localhost:27017/graphql', { useNewUrlParser: true })

.then(() => {

console.log('OK!', 'Mongoose Connected.', 'Version:', mongoose.version)


const bodyParser = require('body-parser')

const app = require('express')()


// 使用 body-parser 中间件

app.use(bodyParser.urlencoded({ extended: false }))

app.use(bodyParser.json())


// // 引入 REST API 业务接口

// app.use('/api/diseases', require('./routes/diseases'))


// // 引入 GraphQL 业务接口

// // 本接口为测试使用

// app.use('/api/graphql/test', require('express-graphql')({

// schema: require('./graphql/schemas/RootSchema'),

// graphiql: true,

// }))

// // 本接口为正式使用

// app.use('/api/graphql', require('express-graphql')({

// schema: require('./graphql/schemas/RootSchema'),

// graphiql: false,

// }))


const html = `

<!DOCTYPE

html>

<head>

<title>

接口服务

</title>

</head>

<br><br><br><br><br>

<center>

<h1>服务正常</h1>

<h1>感谢使用</h1>

<h1><font

color="red">大仙出游</font></h1>

</center>

`


//

测试消息

app.get('/', (req, res) => res.send(html))


// 监听服务

const host = '0.0.0.0'

const port = 3001

app.listen(port, host, () => { console.log(`Server listening on http://localhost:${port} http://${host}:${port}`)

})

})

.catch((err) => {

console.log(err)

})

6.测试程序能否正常运行:终端里执行:npm run serve,出现

OK! Mongoose Connected. Version: 5.5.2

Server listening on http://localhost:3001 http://0.0.0.0:3001

说明正常,打开浏览器,输入http://localhost:3001并回车,出现

说明服务正常。


三、开发 REST API 项目

1、新建目录models,用于存放模型,在目录内创建文件,名为Disease.js,定义集合结构,并编辑内容为:

const mongoose = require('mongoose')

const Schema = mongoose.Schema


// Create Schema

const ObjectSchema = new Schema({

code: {

type: String,

required: true

},

assistant: {

type: String

},

name: {

type: String,

required: true

}

})


module.exports = Disease = mongoose.model('base.diseases', ObjectSchema)

2、新建目录routes,用于存放路由文件,在目录内创建文件名,为diseases.js,实现相关路由,并编辑内容为:

// @POST & GET & PUT & DELETE => CRUD

const express = require('express')

const router = express.Router()

const object = require('../models/Disease')


router.get(

'/test',

(req, res) => {

res.json('OK')

}

)


router.get(

'/',

(req, res) => {

object

.find()

.then(data => {

if (!data)

{

res.status(404).json({ msg: '未查到数据,请先添加!' })

} else {

res.json(data)

}

})

.catch(err => res.status(404).json(err))

}

)


router.get(

'/:id',

(req, res) => {

const id = req.params.id

console.log(id)

object

.findOne({ _id: id })

.then(data => {

if (!data)

{

res.status(404).json({ msg: `未查到 ${id}数据` })

} else {

res.json(data)

}

})

.catch(err => res.status(404).json(err))

}

)

3、测试REST路由能否正常使用,在server.js中,把下面的

// // 引入 REST API 业务接口

// app.use('/api/diseases', require('./routes/diseases'))

取消注释,会发现终端窗口自动编译并重启服务,然后在浏览器中输入:http://localhost:3001/api/diseases,将会获取所有数据,你从网页上复制一个_id值,再输入:http://localhost:3001/api/diseases/5c8d18e4e905ba32d039cd8f,会获取单个数据。说明路由正常

四、开发graphql项目

1、在graphql-demo目录下新建目录graphql,在graphql目录下新建types目录,用于存放类型文件。在types目录中新建Disease.js文件,内容如下:

const {

GraphQLObjectType,

GraphQLString

} = require('graphql')


const objectType = new GraphQLObjectType({

name: 'Disease',

fields: {

_id: {

type: GraphQLString

},

code: {

type: GraphQLString

},

assistant: {

type: GraphQLString

},

name: {

type: GraphQLString

}

}

})


module.exports = objectType

2、在graphql-demo目录下的graphql目录下新建schemas目录,和types同级,用于存放架构文件。在schemas目录中新建RootSchema.js文件,内容如下

const objectDiseaseModel = require('../../models/Disease')

const objectDiseaseType = require('../types/Disease')


const {

GraphQLObjectType,

GraphQLString,

GraphQLInt,

GraphQLList,

GraphQLSchema

} = require('graphql')


const rootSchema = new GraphQLObjectType({

name: 'root',

fields: {

getDisease: {

type: objectDiseaseType,

args: {

id: { type: GraphQLString }

},

async resolve(parent, args)

{

return await objectDiseaseModel.findOne({ "_id": args.id })

}

},

getDiseases: {

type: GraphQLList(objectDiseaseType),

async resolve(parent, args)

{

return await objectDiseaseModel.find({})

}

},

getDiseasesByPagesAndSizes: {

type: GraphQLList(objectDiseaseType),

args: {

pages: { type: GraphQLInt, defaultValue: 1 },

sizes: { type: GraphQLInt, defaultValue: 10 }

},

async resolve(parent, args)

{

return await objectDiseaseModel

.find()

.sort({ code: 1 })

.skip((args.pages - 1)

* args.sizes)

.limit(args.sizes)

}

},

}

})


module.exports = new GraphQLSchema({ query: rootSchema })

3、测试路由能否正常使用,在server.js中的

// // 引入 GraphQL 业务接口

// // 本接口为测试使用

// app.use('/api/graphql/test', require('express-graphql')({

// schema: require('./graphql/schemas/RootSchema'),

// graphiql: true,

// }))

// // 本接口为正式使用

// app.use('/api/graphql', require('express-graphql')({

// schema: require('./graphql/schemas/RootSchema'),

// graphiql: false,

// }))

取消注释,会发现命令行窗口自动编译并重启服务,然后在浏览器中输入:http://localhost:3001/api/graphql/test,出现:



说明路由正常。单击右边的Docs,出现:


单击红色的query右边的root,出现:


然后在最左边的框中输入:{

  getDiseasesByPagesAndSizes(pages:1,sizes:5){

    code

  }

}

然后单击运行,出现:


再次编辑左边的内容,再执行,出现:


说明服务正常,你再加个assistant试下效果:


说明,查询的字段,可以根据实际需要进行自定义了。同理添加其它服务即可。

五、在第三方或当前应用程序中调用graphql,本文中将使用EJS引擎,并且在当前项目中实现

1.安装EJS依赖包,终端执行:npm i ejs

2.在根目录下创建views目录,在views目录下创建test.ejs,内容如下:(error.ejs,自己写吧)

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width,

initial-scale=1.0">

<meta http-equiv="X-UA-Compatible" content="ie=edge">

<title>GraphQL

Test</title>

</head>

<body>

<h1>GraphQL

Test</h1>

<h2>Author:<%=author%></h2>

<h2>Message:<%=message%></h2>

<%if(disease){%>

<p>disease.code:<%=disease.code%></p>

<p>disease.name:<%=disease.name%></p>

<%}%>

<%if(diseasesplit){%>

<h1>Disease

Array</h1>

<ul>

<%

diseasesplit.forEach((disease)=>{ %>

<li><%=disease._id%>

- <%=disease.code%>

- <%=disease.name%></li>

<%

}) %>

</ul>

<%}%>

</body>

</html>


3.在const html上边增加如下代码

// 测试接口,注意get参数用反引号,其中不要有空格,否则会有%编码,id的值要从数据库中找一个真实存在的即可

app.set('view engine', 'ejs')

const axios = require('axios')

app.get(

'/test',

(req, res) => {

axios

.get(`http://localhost:3001/api/graphql/?query={getDisease(id:"5c8d18e4e905ba32d039cd7d"){code,name}getDiseasesByPagesAndSizes(pages:2,sizes:5){_id,code,name}}`)

.then((response) => {

console.log(response.data.data.getDisease)

console.log(response.data.data.getDiseasesByPagesAndSizes)

res.render('test',

{ author: 'Daisen

Travel', message: 'Hello

there!', disease: response.data.data.getDisease, diseasesplit: response.data.data.getDiseasesByPagesAndSizes })

})

.catch((error) => {

console.log(error)

res.render('error',

{ error })

})

})

然后在浏览器输入地址:http://localhost:3001/test,在控制台将看到:

说明调用成功。看到页面


有数据,说明一切OK了!


再添加一个根据id查询的,代码如下:

app.get(

'/test/:id',

(req, res) => {

const { id } = req.params

axios

.get(`http://localhost:3001/api/graphql/?query={getDisease(id:"${id}"){code,name}getDiseasesByPagesAndSizes(pages:2,sizes:5){_id,code,name}}`)

.then((response) => {

console.log(response.data.data.getDisease)

res.render('test',

{ author: 'Daisen

Travel', message: 'Hello

there!', disease: response.data.data.getDisease, diseasesplit: response.data.data.getDiseasesByPagesAndSizes })

})

.catch((error) => {

console.log(error)

res.render('error',

{ error })

})

})

浏览器调用:http://localhost:3001/test/5c8d18e4e905ba32d039c84f,如下界面:


再添加一个分页的,这次只查分页数据了,代码如下:

app.get(

'/test/:pages/:sizes',

(req, res) => {

const { pages, sizes } = req.params

axios

// .get(`http://localhost:3001/api/graphql/?query={getDisease(id:"5c8d18e4e905ba32d039cd7d"){code,name}getDiseasesByPagesAndSizes(pages:${pages},sizes:${sizes}){_id,code,name}}`)

.get(`http://localhost:3001/api/graphql/?query={getDiseasesByPagesAndSizes(pages:${pages},sizes:${sizes}){_id,code,name}}`)

.then((response) => {

// console.log(response.data.data.getDisease)

res.render('test',

{ author: 'Daisen

Travel', message: 'Hello

there!', disease: response.data.data.getDisease, diseasesplit: response.data.data.getDiseasesByPagesAndSizes })

})

.catch((error) => {

console.log(error)

res.render('error',

{ error })

})

})

浏览器调用:http://localhost:3001/test/4/10,界面如下:


总结,GraphQL是一个非常灵活的查询,随时适应需求变化,再也不用为无休止的需求而烦恼了!立刻马上转向GraphQL吧!


祝您成功!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343