Visual Studio Code開發(fā)基于Nodejs的REST和GraphQL 實(shí)戰(zhàn)

優(yōu)勢(shì):按需請(qǐng)求砍艾,不多不少悯衬!

一码耐、環(huán)境配置

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

2.安裝MongoDB的最新Windows或Linux版本承載數(shù)據(jù)追迟,把數(shù)據(jù)導(dǎo)入graphql數(shù)據(jù)庫中,集合名稱為:base.diseases骚腥,是國(guó)家標(biāo)準(zhǔn)疾病庫字典敦间,本文演示數(shù)據(jù)只有很少的三個(gè)鍵值對(duì):code,name,assistant,如果你有1024個(gè)束铭,則慢慢加吧廓块!不過我有個(gè)工具可以批量秒生成!

3.安裝NodeJS的最新版本用于承載項(xiàng)目契沫。

4.升級(jí)npm到最新版本带猴,執(zhí)行:npm i -g npm

5.找個(gè)美好的地方創(chuàng)建graphql-demo目錄,環(huán)境配置完畢懈万。

二拴清、初始化項(xiàng)目

1.打開Visual Studio Code開發(fā)工具,并立即打開剛剛創(chuàng)建好的graphql-demo目錄会通。

2.在菜單“查看”中單擊“終端”口予,或者按Ctrl+`,就是波浪線鍵上靠下邊的那個(gè)反引號(hào)涕侈,打開終端沪停,執(zhí)行:npm init -y,會(huì)出現(xiàn)package.js文件驾凶,說明創(chuàng)建成功牙甫,注意觀察一下此文件的內(nèi)容掷酗,具體內(nèi)容請(qǐng)查閱網(wǎng)絡(luò)資料吧,這里不再闡述窟哺。

3.安裝應(yīng)用程序相關(guān)的必須依賴包(注意用空隔隔開):npm i nodemon mongoose express express-graphql graphql body-parser泻轰,安裝完畢后,在package.js中會(huì)有如下內(nèi)容:

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,內(nèi)容如下旋奢,你直接拷貝一下即可泳挥。

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 業(yè)務(wù)接口

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


// // 引入 GraphQL 業(yè)務(wù)接口

// // 本接口為測(cè)試使用

// 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>

接口服務(wù)

</title>

</head>

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

<center>

<h1>服務(wù)正常</h1>

<h1>感謝使用</h1>

<h1><font

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

</center>

`


//

測(cè)試消息

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


// 監(jiān)聽服務(wù)

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.測(cè)試程序能否正常運(yùn)行:終端里執(zhí)行:npm run serve,出現(xiàn)

OK! Mongoose Connected. Version: 5.5.2

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

說明正常至朗,打開瀏覽器屉符,輸入http://localhost:3001并回車,出現(xiàn)

說明服務(wù)正常锹引。


三矗钟、開發(fā) REST API 項(xiàng)目

1、新建目錄models嫌变,用于存放模型吨艇,在目錄內(nèi)創(chuàng)建文件,名為Disease.js腾啥,定義集合結(jié)構(gòu)东涡,并編輯內(nèi)容為:

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倘待,用于存放路由文件疮跑,在目錄內(nèi)創(chuàng)建文件名,為diseases.js延柠,實(shí)現(xiàn)相關(guān)路由祸挪,并編輯內(nèi)容為:

// @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:?'未查到數(shù)據(jù)锣披,請(qǐng)先添加贞间!'?})

} 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}數(shù)據(jù)`?})

} else?{

res.json(data)

}

})

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

}

)

3、測(cè)試REST路由能否正常使用雹仿,在server.js中增热,把下面的

// // 引入 REST API 業(yè)務(wù)接口

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

取消注釋,會(huì)發(fā)現(xiàn)終端窗口自動(dòng)編譯并重啟服務(wù)胧辽,然后在瀏覽器中輸入:http://localhost:3001/api/diseases峻仇,將會(huì)獲取所有數(shù)據(jù),你從網(wǎng)頁上復(fù)制一個(gè)_id值邑商,再輸入:http://localhost:3001/api/diseases/5c8d18e4e905ba32d039cd8f摄咆,會(huì)獲取單個(gè)數(shù)據(jù)凡蚜。說明路由正常

四、開發(fā)graphql項(xiàng)目

1吭从、在graphql-demo目錄下新建目錄graphql朝蜘,在graphql目錄下新建types目錄,用于存放類型文件涩金。在types目錄中新建Disease.js文件谱醇,內(nèi)容如下:

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同級(jí)副渴,用于存放架構(gòu)文件。在schemas目錄中新建RootSchema.js文件全度,內(nèi)容如下

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煮剧、測(cè)試路由能否正常使用,在server.js中的

// // 引入 GraphQL 業(yè)務(wù)接口

// // 本接口為測(cè)試使用

// 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,

// }))

取消注釋将鸵,會(huì)發(fā)現(xiàn)命令行窗口自動(dòng)編譯并重啟服務(wù)轿秧,然后在瀏覽器中輸入:http://localhost:3001/api/graphql/test,出現(xiàn):



說明路由正常咨堤。單擊右邊的Docs菇篡,出現(xiàn):


單擊紅色的query右邊的root,出現(xiàn):


然后在最左邊的框中輸入:{

??getDiseasesByPagesAndSizes(pages:1,sizes:5){

????code

??}

}

然后單擊運(yùn)行一喘,出現(xiàn):


再次編輯左邊的內(nèi)容驱还,再執(zhí)行,出現(xiàn):


說明服務(wù)正常凸克,你再加個(gè)assistant試下效果:


說明议蟆,查詢的字段,可以根據(jù)實(shí)際需要進(jìn)行自定義了萎战。同理添加其它服務(wù)即可咐容。

五、在第三方或當(dāng)前應(yīng)用程序中調(diào)用graphql蚂维,本文中將使用EJS引擎戳粒,并且在當(dāng)前項(xiàng)目中實(shí)現(xiàn)

1.安裝EJS依賴包,終端執(zhí)行:npm i ejs

2.在根目錄下創(chuàng)建views目錄虫啥,在views目錄下創(chuàng)建test.ejs蔚约,內(nèi)容如下:(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上邊增加如下代碼

// 測(cè)試接口涂籽,注意get參數(shù)用反引號(hào)苹祟,其中不要有空格,否則會(huì)有%編碼,id的值要從數(shù)據(jù)庫中找一個(gè)真實(shí)存在的即可

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树枫,在控制臺(tái)將看到:

說明調(diào)用成功直焙。看到頁面


有數(shù)據(jù)砂轻,說明一切OK了箕般!


再添加一個(gè)根據(jù)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?})

})

})

瀏覽器調(diào)用:http://localhost:3001/test/5c8d18e4e905ba32d039c84f舔清,如下界面:


再添加一個(gè)分頁的丝里,這次只查分頁數(shù)據(jù)了,代碼如下:

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?})

})

})

瀏覽器調(diào)用:http://localhost:3001/test/4/10体谒,界面如下:


總結(jié)杯聚,GraphQL是一個(gè)非常靈活的查詢,隨時(shí)適應(yīng)需求變化抒痒,再也不用為無休止的需求而煩惱了幌绍!立刻馬上轉(zhuǎn)向GraphQL吧!


祝您成功故响!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末傀广,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子彩届,更是在濱河造成了極大的恐慌伪冰,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件樟蠕,死亡現(xiàn)場(chǎng)離奇詭異贮聂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)寨辩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門吓懈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人靡狞,你說我怎么就攤上這事耻警。” “怎么了甸怕?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵甘穿,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我蕾各,道長(zhǎng)扒磁,這世上最難降的妖魔是什么庆揪? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任式曲,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吝羞。我一直安慰自己兰伤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布钧排。 她就那樣靜靜地躺著敦腔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪恨溜。 梳的紋絲不亂的頭發(fā)上符衔,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音糟袁,去河邊找鬼判族。 笑死,一個(gè)胖子當(dāng)著我的面吹牛项戴,可吹牛的內(nèi)容都是我干的形帮。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼周叮,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼辩撑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仿耽,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤合冀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后项贺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體水慨,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年敬扛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晰洒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡啥箭,死狀恐怖谍珊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情急侥,我是刑警寧澤砌滞,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站坏怪,受9級(jí)特大地震影響贝润,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜铝宵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一打掘、第九天 我趴在偏房一處隱蔽的房頂上張望华畏。 院中可真熱鬧,春花似錦尊蚁、人聲如沸亡笑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仑乌。三九已至,卻和暖如春琴锭,著一層夾襖步出監(jiān)牢的瞬間晰甚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工决帖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留压汪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓古瓤,卻偏偏與公主長(zhǎng)得像止剖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子落君,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361