教你用Node.js與Express建立一個(gè)GraphQL服務(wù)器
前言
在這篇文章中,我們將對(duì)使用Node.js和Express建立GraphQL服務(wù)器的整個(gè)過(guò)程進(jìn)行演練。我們將使用 Express 的中間件庫(kù)express-graphql 來(lái)協(xié)助我們完成這一過(guò)程。
如果你還不熟悉GraphQL以及我們使用它的目的,請(qǐng)務(wù)必查看這篇文章,我們將深入了解GraphQL是什么以及為什么我們應(yīng)該在我們的應(yīng)用程序中使用它。
如果你已經(jīng)熟悉它了,你可能想看看這篇文章,在這篇文章中我們用React實(shí)現(xiàn)了Apollo客戶端來(lái)連接到我們現(xiàn)在要?jiǎng)?chuàng)建的服務(wù)器。
包含這篇文章中的代碼的資源庫(kù)可以在這里找到。
還有一個(gè)包含使用該服務(wù)器的前端的資源庫(kù),可以在這里找到。
所以,不用多說(shuō)了,讓我們開(kāi)始吧。
GraphQL服務(wù)器配置設(shè)置
首先,我們可能想先創(chuàng)建一個(gè)新的目錄,通過(guò)以下命令設(shè)置npm npm init命令,然后創(chuàng)建我們的 server.js文件,該文件將承載我們的GraphQL服務(wù)器。
一旦我們完成了這些,我們就需要安裝以下庫(kù):
- express
- express-graphql
- graphql
- cors
server.js文件看起來(lái)應(yīng)該是這樣的:
const app = require("express")(); const cors = require('cors'); const { graphqlHTTP } = require("express-graphql"); const { buildSchema } = require("graphql"); const schema = buildSchema(''); const root = { }; app.use(cors()); app.use( "/graphql", graphqlHTTP({ schema, rootValue: root, graphiql: true, }) ); app.listen(8080, () => { console.log('GraphQL server running on port 8080'); });
讓我們來(lái)看看配置過(guò)程的每一步,這樣我們就能更好地了解實(shí)際發(fā)生的情況:
- 首先導(dǎo)入express并調(diào)用其導(dǎo)出的主函數(shù),這樣我們就可以通過(guò)app變量來(lái)設(shè)置我們的應(yīng)用服務(wù)器了
- 導(dǎo)入cors庫(kù)的主函數(shù),以幫助我們解決在不同域(localhost與不同端口)上運(yùn)行服務(wù)器的問(wèn)題
- 從Express-graphql庫(kù)中導(dǎo)入graphqlHTTP方法,幫助我們進(jìn)行配置
- 從graphql庫(kù)中導(dǎo)入buildSchema方法來(lái)定義數(shù)據(jù)模式(我們?cè)试S客戶訪問(wèn)哪些數(shù)據(jù))。
- 現(xiàn)在定義一個(gè)空的數(shù)據(jù)模式
- 現(xiàn)在定義一個(gè)空的根,但這將被用來(lái)定義我們的 調(diào)解器或者說(shuō)我們選擇如何處理數(shù)據(jù),以便將其發(fā)送到客戶端。
- 設(shè)置 CORS,不需要額外的配置
- 定義我們將用于**"/graphql "**端點(diǎn)的配置,它將利用我們之前定義的模式、根解析器對(duì)象,以及我們將用于測(cè)試和與我們的數(shù)據(jù)進(jìn)行可視化交互的游樂(lè)場(chǎng)。
為了啟動(dòng)服務(wù)器,你所需要做的就是執(zhí)行 node server.js命令。然后你可以在你的瀏覽器中檢查 "localhost:8080",在那里你就可以看到GraphiQL游樂(lè)場(chǎng)的運(yùn)行,這是一種與你的數(shù)據(jù)/架構(gòu)進(jìn)行交互的可視化方式。
它看起來(lái)應(yīng)該是這樣的。
在瀏覽器中運(yùn)行的GraphiQL
定義模式
現(xiàn)在我們已經(jīng)把GraphQL設(shè)置好了,讓我們看看用一些數(shù)據(jù)來(lái)填充我們的GraphQL模式。
為了簡(jiǎn)單起見(jiàn),我們將使用一些更基本的模型,并在這些模型之間定義一個(gè)基本關(guān)系。我們將有一個(gè)作者模型和一個(gè)圖書(shū)模型,它們將有以下模式:
書(shū)籍模式:
作者模式
正如你從所附圖片中看到的,我們將有2個(gè)基本模型,分別有3個(gè)和4個(gè)字段,在這里我們也定義了一對(duì)多的關(guān)系。這意味著一個(gè)作者可以寫(xiě)很多書(shū),而一本書(shū)只能有一個(gè)作者。
這就是我們?nèi)绾卧诖a中定義這些模型模式:
const schema = buildSchema( ` type Query { authors: [Author] books: [Book] } type Author { id: String firstName: String! lastName: String! books: [Book] } type Book { id: String title: String! author: Author } ` );
我們還必須定義我們的父查詢類型,它承載了所有檢索數(shù)據(jù)的查詢。在定義了父查詢之后,我們將不得不為作者和書(shū)的模型定義模式。
你可以注意到一些字段類型后面的"!"符號(hào),這表示一個(gè)字段是不可置空的。方括號(hào)("[]")圍繞著一個(gè)類型,意味著它是一個(gè)數(shù)組。
嘲弄我們的數(shù)據(jù)
我們現(xiàn)在已經(jīng)定義了模式,但還缺少兩樣?xùn)|西:數(shù)據(jù)和解析器來(lái)解析查詢的數(shù)據(jù)。
首先,讓我們根據(jù)之前定義的模式來(lái)模擬我們的數(shù)據(jù):
const mockedAuthors = [ { id: '1', firstName: "Mike", lastName: "Ross", }, { id: '2', firstName: "John", lastName: "Miles", books: [ { id: '1', title: "Book 1", author: { id: '2', firstName: "John", lastName: "Miles", }, }, { id: '2', title: "Book 2", author: { id: '2', firstName: "John", lastName: "Miles", }, }, ], }, ]; const mockedBooks = { '1': { title: "Book 1", author: mockedAuthors["2"], }, '2': { title: "Book 2", author: mockedAuthors["2"], }, };
正如你可能注意到的,"Mike Ross "還沒(méi)有寫(xiě)任何書(shū),這不是一個(gè)問(wèn)題,因?yàn)樽髡吣J讲恍枰谄鋽?shù)組內(nèi)有任何書(shū)。此外,根本不需要定義一個(gè)書(shū)的數(shù)組,因?yàn)槟J讲恍枰?。然而,我們可以通過(guò)改變作者的書(shū)籍字段的類型來(lái)改變這種情況。[書(shū)籍]!這就要求至少有一個(gè)數(shù)組,不管是不是空的。
定義解析器
現(xiàn)在我們已經(jīng)定義了模式和數(shù)據(jù),我們可以通過(guò)定義解析器來(lái)完成,我們將使用這些解析器來(lái)實(shí)際處理如何將數(shù)據(jù)傳遞給客戶端。
現(xiàn)在,我們只需要兩個(gè)解析器:
- 一個(gè)用于解析作者的數(shù)據(jù)
- 一個(gè)用于解析圖書(shū)數(shù)據(jù)
我們將在根中這樣定義解析器:
const root = { authors: () => mockedAuthors, books: () => mockedBooks, };
正如你在上面的代碼片段中所看到的,定義解析器的過(guò)程是非常直接的。會(huì)有一個(gè)作為查詢名稱的屬性,這意味著通過(guò)具體查詢 "author",都是小寫(xiě)字母,你會(huì)得到 "mockedAuthors "數(shù)據(jù)。
注意,我們還將函數(shù)作為值傳遞給屬性,這一點(diǎn)很關(guān)鍵。
現(xiàn)在讓我們測(cè)試一下所有的東西,這樣我們就可以確保它按預(yù)期工作:
正如你所看到的,這一切都像預(yù)期的那樣工作正常。我們使用GraphQL的特定術(shù)語(yǔ),提到我們正在做一個(gè)查詢,使用 "query "關(guān)鍵字,后面是我們希望查詢的屬性/類型/條目。在這種情況下,我們希望查詢作者類型的所有屬性,但只查詢書(shū)籍類型的id和標(biāo)題;我們跳過(guò)了作者字段。
因?yàn)槲覀円呀?jīng)模擬了數(shù)據(jù),以包括書(shū)籍中的作者,我們可能會(huì)做像這樣的循環(huán)查詢。
所以,你有了它。你可以看到設(shè)置GraphQL服務(wù)器并為其定義模式和解析器是多么容易。通過(guò)定義類型,你不僅有驗(yàn)證的地方,而且你還可能從你所選擇的游樂(lè)場(chǎng)訪問(wèn)文檔。
GraphiQL提供了一個(gè)右上方的小Docs菜單,它可以跳出來(lái),向你展示在模式中定義的所有類型的文檔。
GraphiQL中的GraphQL自我文檔化模式
現(xiàn)在我們已經(jīng)建立了使用查詢檢索數(shù)據(jù)的工作流程,我們還將看看如何使用突變來(lái)修改已有的數(shù)據(jù)。
定義突變
當(dāng)我們使用查詢來(lái)檢索數(shù)據(jù)時(shí),我們使用突變來(lái)創(chuàng)建、修改或刪除現(xiàn)有數(shù)據(jù)。
假設(shè)我們要?jiǎng)?chuàng)建一個(gè)新的作者條目,我們要做的就是定義一個(gè)新的父 "突變"類型,該類型將擁有我們?cè)谡麄€(gè)系統(tǒng)中可能使用的突變字段。在那之后,我們必須為它定義解析器,然后就可以了。這將是我們需要做的所有事情。
下面的代碼片段應(yīng)該說(shuō)明最終的server.js文件具有創(chuàng)建一個(gè)新的作者條目的能力:
const app = require("express")(); const cors = require('cors'); const { graphqlHTTP } = require("express-graphql"); const { buildSchema } = require("graphql"); const schema = buildSchema( ` type Query { authors: [Author] books: [Book] } type Mutation { createAuthor( firstName: String!, lastName: String! ): Author } type Author { id: String firstName: String! lastName: String! books: [Book] } type Book { id: String title: String! author: Author } ` ); const mockedAuthors = [ { id: '1', firstName: "Mike", lastName: "Ross", }, { id: '2', firstName: "John", lastName: "Miles", books: [ { id: '1', title: "Book 1", author: { id: '2', firstName: "John", lastName: "Miles", }, }, { id: '2', title: "Book 2", author: { id: '2', firstName: "John", lastName: "Miles", }, }, ], }, ]; const mockedBooks = { '1': { title: "Book 1", author: mockedAuthors["2"], }, '2': { title: "Book 2", author: mockedAuthors["2"], }, }; const root = { authors: () => mockedAuthors, books: () => mockedBooks, createAuthor: ({ firstName, lastName }) => { const id = String(mockedAuthors.length + 1); const createdAuthor = { id, firstName, lastName }; mockedAuthors.push(createdAuthor); return createdAuthor; } }; app.use(cors()); app.use( "/graphql", graphqlHTTP({ schema, rootValue: root, graphiql: true, }) ); app.listen(8080);
所以,你有了它。你應(yīng)該有一個(gè)工作的GraphQL服務(wù)器,你可以與之互動(dòng),以檢索和修改或刪除數(shù)據(jù)。
你可能會(huì)選擇開(kāi)始研究將解析器和模式類型分離到單獨(dú)的目錄和文件中,并在server.js文件中聚合它們,還有更多;在考慮擴(kuò)展GraphQL應(yīng)用程序時(shí),選擇是無(wú)窮無(wú)盡的。畢竟,這是它被建立的原因之一。
我希望你喜歡閱讀這篇文章,并希望它能幫助你了解使用Node.js和Express設(shè)置GraphQL服務(wù)器的基本原理。
總結(jié)
到此這篇關(guān)于用Node.js與Express建立一個(gè)GraphQL服務(wù)器的文章就介紹到這了,更多相關(guān)Node.js和Express建立GraphQL服務(wù)器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
node快速搭建后臺(tái)的實(shí)現(xiàn)步驟
本文主要介紹了node快速搭建后臺(tái),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12Node做中轉(zhuǎn)服務(wù)器轉(zhuǎn)發(fā)接口
這篇文章主要介紹了Node做中轉(zhuǎn)服務(wù)器轉(zhuǎn)發(fā)接口的相關(guān)資料,需要的朋友可以參考下2017-10-10Nodejs使用exceljs實(shí)現(xiàn)excel導(dǎo)入導(dǎo)出
在日常開(kāi)發(fā)中,我們常需在后臺(tái)管理系統(tǒng)中實(shí)現(xiàn)數(shù)據(jù)的導(dǎo)入與導(dǎo)出功能,以便與?Excel?文件進(jìn)行交互,本文將使用使用exceljs實(shí)現(xiàn)excel導(dǎo)入導(dǎo)出功能,需要的可以參考下2024-03-03NPM命令運(yùn)行報(bào)錯(cuò):npm?v10.2.4?is?known?not?to?run?on?Node.js
這篇文章主要給大家介紹了關(guān)于NPM命令運(yùn)行報(bào)錯(cuò):npm?v10.2.4?is?known?not?to?run?on?Node.js?v14.21.1的解決辦法,文中將解決辦法介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01node-sass常見(jiàn)報(bào)錯(cuò)的問(wèn)題及解決方法
在安裝node-sass時(shí),經(jīng)常會(huì)遇到下載慢、版本不匹配或python錯(cuò)誤等問(wèn)題,使用淘寶鏡像加速下載、通過(guò)nvm管理node版本或指定node-sass版本號(hào)安裝,都能有效解決這些問(wèn)題,若遇到python相關(guān)錯(cuò)誤,檢查node版本是否合適通??梢越鉀Q,感興趣的朋友一起看看本文吧2024-09-09Node.js與PHP、Python的字符處理性能對(duì)比
因?yàn)楹罄m(xù)考慮實(shí)現(xiàn) Fl 引擎的Node.js版本,所以對(duì)比了下Node.js和PHP的字符處理性能。發(fā)現(xiàn)Node.js真是甩了PHP幾條街啊,再測(cè)試了下Python,比PHP還慢。2014-07-07