【前端大佬 | Node 連載 3/9】預(yù)策科技 - 芋頭:《如何高效開發(fā)表現(xiàn)層 Node.js 應(yīng)用》
第 63 屆早早聊大會(huì)將于 2023 年 4 月 15 日(本周六)舉辦 - 低代碼搭建|無碼有碼 全靠拖拉,6 位講師全天直播,關(guān)鍵詞:低代碼/組件物料/游戲?qū)嵺`/ AB Test /React 玩轉(zhuǎn)。跟早早聊一起,玩轉(zhuǎn)低代碼,上車鏈接:https://www.zaozao.run/conf/c63


本文是 2021 年 12 月 26 日,第三十五屆 - 前端早早聊【前端搞 Node.js】專場(chǎng),來自預(yù)測(cè)科技的技術(shù)總監(jiān) —— 芋頭的分享。感謝 AI 的發(fā)展,借助 GPT 的能力,最近我們終于可以非常高效地將各位講師的精彩分享文本化后,分享給大家。(完整版含演示請(qǐng)看錄播視頻和 PPT):https://www.zaozao.run/video/c35
正文如下
大家好,我是芋頭。今天分享的主題是《如何使用 Node.js 進(jìn)行前端應(yīng)用的開發(fā)》,涉及到表現(xiàn)層應(yīng)用的開發(fā)。我今天分享的方案是針對(duì)簡(jiǎn)單場(chǎng)景的,旨在讓前端開發(fā)人員不必掌握太多關(guān)于 Node.js 的背景知識(shí)和專業(yè)知識(shí),即使沒有代碼編寫經(jīng)驗(yàn),也能完成一些簡(jiǎn)單的服務(wù)端開發(fā)任務(wù)。
我很多小公司都面臨著這樣的情況:大公司的開發(fā)人員更喜歡專注于專業(yè),需要掌握 ORM、服務(wù)器和運(yùn)維等專業(yè)知識(shí),但在小公司,我們可以直接上手完成開發(fā)任務(wù)。因此,我的方案是介紹一系列工具的組合,這些工具是我在創(chuàng)業(yè)公司里的一些嘗試,我想簡(jiǎn)單地和大家分享一下。
這里展示的內(nèi)容實(shí)際上就是我今天想講的全部?jī)?nèi)容。

底層是 Nest.js 開發(fā)框架,實(shí)際上,使用什么框架不是很重要,因?yàn)樗惶峁┳罨镜墓δ埽@些功能是所有框架都具有的。Nest.js 封裝了網(wǎng)絡(luò)層和應(yīng)用分層,在國(guó)外使用較多,因?yàn)樗拈_發(fā)方式與 Spring Boot 類似,所以對(duì)于熟悉 Java 的人來說很熟悉。
上層使用的是名為 Prisma 的 ORM,它是一個(gè)比較特殊的 ORM,是一個(gè)比較純粹的 ORM,是 Model 層的 ORM。我今天的整個(gè)方案是通過 Prisma 將所有開發(fā)流程串聯(lián)起來,將多個(gè)工具連接在一起?;旧?,你只需要寫很少的代碼,就可以完成從底層數(shù)據(jù)層到頂層控制層再到整個(gè) API 文檔的應(yīng)用開發(fā)方案。
我最近比較關(guān)注的是這種漸進(jìn)增強(qiáng)的方案,很多國(guó)外社區(qū)的方案都注重這一點(diǎn)。也就是說,這些方案都是可插拔的,包括我今天講的內(nèi)容,你可以選擇使用或者不使用,或者只使用其中的一部分,而不會(huì)影響其他開發(fā)工作。所以,如果你的公司還比較小,而且想讓前端負(fù)責(zé)這部分工作,你可以讓他們簡(jiǎn)單地使用這些工具。但是當(dāng)你的公司或者團(tuán)隊(duì)發(fā)展之后,你可能需要專業(yè)的 Node.js 開發(fā)人員,他們了解 ORM、數(shù)據(jù)庫(kù)、緩存等知識(shí)。這時(shí)你可以卸載上層的工具,使用 Nest.js,甚至將 Nest.js 替換成其他框架。這是一種靈活的思路,上層工具的耦合度很低,是可替換的。
這就是我今天要介紹的三個(gè)核心工具, Nest.js,Prisma,GraphQL。這三者都非常重要。


Prisma
首先簡(jiǎn)單介紹一下 Prisma,Prisma 在官網(wǎng)上的介紹是它是「Next-generation Node.js and TypeScript ORM」,強(qiáng)調(diào)了它對(duì)類型的強(qiáng)大支持。
關(guān)于 Prisma 的使用,實(shí)際上它是一個(gè) ORM 工具,但它的重點(diǎn)并不是讓你自己去定義模型、類等等,而是在進(jìn)入使用時(shí)首先要定義一個(gè)描述文件。這個(gè)描述文件與熟悉 GraphQL 的同學(xué)應(yīng)該都知道,GraphQL 有兩種開發(fā)方式,一種是架構(gòu)優(yōu)先,一種是代碼優(yōu)先。而 Prisma 作為一個(gè)整套方案,在國(guó)外與 GraphQL 有著緊密的聯(lián)系,但它們之間的耦合并不是特別嚴(yán)格,而是比較輕松的。
因此,在 Prisma 的開發(fā)中,首先要定義一個(gè)描述文件,其中包括
定義 Model(數(shù)據(jù)模型),這是每個(gè) ORM 都需要做的事情。
定義 DataSource(數(shù)據(jù)源),這是因?yàn)樵?Prisma 中,它接管了與數(shù)據(jù)庫(kù)的連接,這一層被包含在整個(gè)庫(kù)中,所以你不需要關(guān)注如何連接數(shù)據(jù)庫(kù),而是由 Prisma 內(nèi)部進(jìn)行數(shù)據(jù)庫(kù)連接的管理。
定義 Generator(生成器),是 Prisma 的核心重點(diǎn)。它定義了數(shù)據(jù)模型和數(shù)據(jù)連接管理方式。Generator 其實(shí)是在定義一些官方和第三方的生成工具,它可以生成強(qiáng)類型的 GraphQL schema 的 JS 包,生成類型的 DTO 的定義,還有 GraphQL Schema 的定義。


所以在使用 Prisma 時(shí),你可能會(huì)發(fā)現(xiàn)它的用法有些奇怪。通常情況下,當(dāng)你使用其他庫(kù)時(shí),你只需要直接引用 Node models 中的 ORM 的代碼。但是,Prisma 中 Node models 的代碼是在運(yùn)行時(shí)動(dòng)態(tài)生成的,它并不是在你下載下來的時(shí)候就已經(jīng)存在的。所以,在使用 Prisma 時(shí),實(shí)際上你并沒有去定義像我剛才在右邊代碼中寫的「this.prisma.tag」這樣的模型,但是你卻可以將它當(dāng)作一個(gè)正常的對(duì)象來使用。
當(dāng)然,Prisma 還有很多其他的特性,我剛才只列舉了一部分。
Prisma 提供了一套數(shù)據(jù)訂正的工具。實(shí)際上很多 ORM 都已經(jīng)在使用這種特性了。但是在一些小型公司或者小型產(chǎn)品中,使用這個(gè)工具其實(shí)非常方便。然而,在一些大型團(tuán)隊(duì)中,使用這種數(shù)據(jù)訂正工具可能會(huì)比較困難。不過,在這方面,Prisma 在這塊的功能實(shí)際上做得比較成熟。
當(dāng)你需要修改一個(gè) Model 中的任何信息時(shí),它都會(huì)幫你生成訂正的語句,而且會(huì)進(jìn)行預(yù)測(cè),例如,如果你之前刪除了一個(gè)字段,而這個(gè)字段里面有信息,并且是必填的,而你現(xiàn)在把它改成了非必填,那么這時(shí)候系統(tǒng)會(huì)生成一條非常復(fù)雜的訂正語句,它會(huì)進(jìn)行預(yù)測(cè)并讓你做出選擇。
因此,在某些小型場(chǎng)景中,這個(gè)數(shù)據(jù)訂正工具非常方便。最后,它會(huì)生成一個(gè)訂正的記錄,并可以將這個(gè)訂正記錄同步到你的 Git 中。關(guān)于在生產(chǎn)環(huán)境中是否需要使用這套工具,這里我就不詳細(xì)展開了。
無碼有碼,全靠拖拉。跟早早聊一起,玩轉(zhuǎn)低代碼,上車鏈接:https://www.zaozao.run/conf/c63

Nest.js
接下來是關(guān)于 Nest.js 框架的簡(jiǎn)單介紹。這個(gè)框架實(shí)際上與許多其他框架非常類似,它實(shí)現(xiàn)了一些分層和全局鉤子等機(jī)制,用于串聯(lián)各種開發(fā)工具。我想要在 Nest.js 中盡量減少編寫邏輯的數(shù)量。
因此,大家最終可以看到,在 Nest.js 端實(shí)現(xiàn)了一個(gè)完整的增刪改查操作,甚至包括復(fù)雜的操作,基本上沒有代碼。因此,我只會(huì)簡(jiǎn)單地介紹一下這個(gè)框架是什么樣的。
Nest.js 框架實(shí)際上有三個(gè)核心概念:Controller,Provider,Moduler。

最底層、最重要的是 Moduler,它是用來組織一個(gè)完整業(yè)務(wù)邏輯中的所有模塊的總?cè)肟?。每個(gè) Moduler 都有一個(gè)完整的分層,入口是 Controller,所有的請(qǐng)求都首先進(jìn)入 Controller 層。Provider 即服務(wù)層,主要提供一些類似于平常編寫的 service 層邏輯的功能。
大家可以看到,這里沒有明確的提到 Model 層。雖然在模型層中有一些定義,但今天我們使用的是 Prisma,它基本上占用了 Model 層。因此,開發(fā)一個(gè) Nest.js 的流程實(shí)際上是一個(gè)正常的開發(fā)流程,即當(dāng)應(yīng)用進(jìn)來時(shí),需要寫 controller,service,定義 DTO 和 VO。
一旦有了 Prisma 之后,我們可以使用 Prisma 生成一個(gè) ORM 的庫(kù) ,同時(shí)它還會(huì)幫助我們生成一些 DTO 和 VO 的代碼,所以我們只要關(guān)注 controller 和 service。此外,Nest.js 還提供了一個(gè)用于增加、刪除、修改、查詢的一個(gè)腳手架。當(dāng)執(zhí)行了這個(gè)命令后,它會(huì)為你生成一個(gè)類似于下面這個(gè)圖中的文件結(jié)構(gòu),然后你只需要填充其中的 controller 和 service 即可。

大家看似簡(jiǎn)單,就是生成了一個(gè) Controller,然后手寫了一些代碼,就是那個(gè) CRUD 工具,但其實(shí)這些是非常初級(jí)的,不能幫你生成邏輯代碼。所以要自己去定義參數(shù)的類型,調(diào)用 Service 層。當(dāng)然,這個(gè) Controller 層的代碼還是比較簡(jiǎn)單的,不需要做太多事情,邏輯都在 Service 層。在這里需要寫增刪改查的邏輯,例如基本的分頁和條件過濾等技術(shù),以及聯(lián)合查詢等。
我不希望前端變成一個(gè)服務(wù)端的開發(fā)者。前端寫服務(wù)端的意義是什么呢?尤其對(duì)于一個(gè)小團(tuán)隊(duì)來說,為什么要這么做呢?其實(shí)我覺得這個(gè)框架的初衷并不是讓前端變成一個(gè)服務(wù)端的開發(fā)者,而且這樣做可能會(huì)遭到后端開發(fā)者的質(zhì)疑,我用 Spring Boot 寫得很好,為什么要用 Nest.js?它有什么本質(zhì)上的改變嗎?其實(shí)并沒有。
所以為什么要讓前端嘗試去做一些服務(wù)端的開發(fā)呢?其實(shí)我初衷是為了提高效率,面對(duì)一個(gè)小的普通界面上的更改,避免前端設(shè)計(jì)師、前端開發(fā)人員和服務(wù)端開發(fā)人員都要參與才能完成此次更改,從而減少效率損耗。我希望前端盡量少碰服務(wù)端的代碼,但同時(shí)又能靈活地調(diào)整接口,以適應(yīng)前端的不斷變更。所以我不希望大家去寫類似于 find、where、select、count 等這樣的代碼,因?yàn)閷?duì)于不太熟悉這些概念的前端來說,很容易寫出問題來,而且我也不希望花太多精力去監(jiān)督他們?nèi)绾螌戇@些東西。

接下來我會(huì)繼續(xù)詳細(xì)介紹。這里,大家可以看到 Prisma 類型是非常強(qiáng)的,不僅僅是模型的類型,還包括模型生成的操作類的類型,以及各種查詢條件的類型,非常精細(xì)。實(shí)際上,所有使用的東西的類型都是強(qiáng)類型,都是根據(jù)模型定義推導(dǎo)出來的。因此,在它的定義中,它標(biāo)明了它是一個(gè) TypeScript 的 ORM,這是它的優(yōu)勢(shì)之一。
如果你要使用 Nest.js,不管你使用什么框架,你都要做一些其他的事情,比如身份驗(yàn)證、異常處理、請(qǐng)求返回的統(tǒng)一封裝和配置等。
網(wǎng)上有一些資料,但有時(shí)候描述得不是很完整。因此,我做了一個(gè)示例,如果大家需要使用的話,可以從我的 GitHub 項(xiàng)目中 clone 出來,這樣可以避免很多坑。
我們剛剛完成了一個(gè)正常的 Nest.js 和 Prisma 的應(yīng)用程序開發(fā),實(shí)際上代碼量不多,對(duì)于我這種之前做過 Node.js 服務(wù)端開發(fā)的人來說沒有太大的壓力。但是對(duì)于一個(gè)前端開發(fā)者來說,我向他介紹這些東西,他可能會(huì)有些難以接受,那是否可以在不編寫代碼的情況下完成復(fù)雜的查詢、插入、更新和刪除邏輯,并且能夠靈活調(diào)整前端界面?
歡迎報(bào)名第 63 屆早早聊大會(huì) - 低代碼無代碼,了解如何通過低代碼平臺(tái)提高生產(chǎn)力,玩轉(zhuǎn)低代碼。上車戳:https://www.zaozao.run/conf/c63

GraphQL
要解決上面的問題,我們可以引入 GraphQL,這實(shí)際上是一種面向前端的接口開放方式。GraphQL 并不是一個(gè)萬能藥,不是說通過 GraphQL 提供的接口就變得高級(jí)了,或者解決了很多問題。實(shí)際上,它只適用于某些場(chǎng)景,特別是它最初設(shè)計(jì)的樹狀數(shù)據(jù)結(jié)構(gòu),而不是圖狀數(shù)據(jù)結(jié)構(gòu),可能在樹狀數(shù)據(jù)結(jié)構(gòu)的場(chǎng)景下更加適合。
但是今天我的話題核心實(shí)際上是 Prisma 和 GraphQL 的配合。雖然 Prisma 的官方定義中沒有提到這一點(diǎn),但是如果你查看官方文檔,它有一個(gè)關(guān)于 Prisma 和 GraphQL 如何一起使用的話題。
在這里我使用了兩個(gè)工具。
prisma-nestjs-graphql
第一個(gè)工具是我要用 Prisma 的 Schema 生成 GraphQL 的 Schema,這個(gè)工具實(shí)在不斷演進(jìn)。我使用的是它之前的一個(gè)版本,prisma-nestjs-graphql,這個(gè)庫(kù)的名字非常直白,就是將這三者連接起來,通過執(zhí)行 Prisma 的 generate 命令,可以在項(xiàng)目中生成內(nèi)容。生成的內(nèi)容不僅包括 Model 的定義(例如 tags),還會(huì)生成一些定義這些操作的參數(shù)。

我的查詢操作非常靈活,包括了 where 條件、in、order by、group by、count、分頁 等操作,而這個(gè)工具可以生成這些操作所需的文件。當(dāng)我生成了這些文件并引入了這個(gè)插件后,我的代碼就發(fā)生了變化。實(shí)際上,這就是我在實(shí)現(xiàn)增刪改查功能的正常代碼。
這里 Resolver 實(shí)際上是用于 GraphQL 開發(fā)的,類似于 Controller 的一個(gè)分層,實(shí)際上 Resolver 內(nèi)的邏輯沒有發(fā)生太大的變化。雖然它與 Controller 的定義有些類似,但是到了 Service 層,你會(huì)發(fā)現(xiàn)一個(gè)神奇的事情,那就是它內(nèi)部沒有代碼。在正常的開發(fā)中,Service 層通常需要編寫調(diào)用 ORM 的代碼,但在這里,基本上是透?jìng)鞯?,你可以直接透?jìng)?update、delete、count 等操作。當(dāng)然,在這里你也可以編寫正常的查詢操作,但在普通情況下,你只需要透?jìng)鬟@些操作 Resolver,Resolver 會(huì)直接透?jìng)鹘o前端。

swagger-to-graphql
在實(shí)際開發(fā)過程中,前端代碼還需要訪問服務(wù)端之前開發(fā)的接口。為了使這些接口與前端代碼的數(shù)據(jù)訪問更好地結(jié)合,需要使用 swagger-to-graphql 工具,它可以將 Swagger 的格式(即 Open API 的標(biāo)準(zhǔn)格式)轉(zhuǎn)換為 Graphql 數(shù)據(jù)模型,使得前端代碼可以直接訪問服務(wù)端的接口。這個(gè)庫(kù)的使用非常簡(jiǎn)單,只需傳入一個(gè) Swagger 文檔,定義一個(gè) Provider,把 Swagger 的數(shù)據(jù)傳進(jìn)來,就會(huì)自動(dòng)生成一個(gè)透?jìng)鞯?GraphQL 接口供使用。可以在 GraphQL 的視圖中看到服務(wù)端定義的接口以及所有可用的查詢參數(shù)。

第 63 屆早早聊大會(huì) - 低代碼無代碼,玩轉(zhuǎn)低代碼。上車戳:https://www.zaozao.run/conf/c63

最后
以上就是我的全部分享,我介紹了最基本的三個(gè)東西,即 Nest.js、Prisma 和 GraphQL,以及我用到的一些工具。
我沒有深入講解 Prisma 框架內(nèi)部如何實(shí)現(xiàn)之前在 Service 里面一行代碼都不寫的狀態(tài),這是因?yàn)?Prisma 本身可以自適應(yīng)到 GraphQL 的 Resolver 上面去,這是普通的 ORM 庫(kù)難以實(shí)現(xiàn)的。另外,今天我將的內(nèi)容都可以在我的 GitHub 倉(cāng)庫(kù)(https://github.com/0xYootou/Nest.js-prisma-example)中找到。如果你對(duì)這個(gè)話題感興趣,可以去找找我的倉(cāng)庫(kù),里面包括了一些鑒權(quán)、錯(cuò)誤處理和返回結(jié)果的封裝,還有關(guān)于 Nest.js 的一些東西,非常方便用于小型應(yīng)用的開發(fā)。

低代碼是當(dāng)今最熱門的技術(shù)之一,如果你對(duì)低代碼感興趣,或者正在研究低代碼,歡迎報(bào)名第 63 屆早早聊大會(huì) - 低代碼無代碼,跟早早聊一起,玩轉(zhuǎn)低代碼。
舉辦時(shí)間:2023 年 4 月 15 日 ?10:00 ~ 17:00
截至?xí)r間:2023 年 4 月 15 日 ?19:00
舉辦方式:微信群 PPT 推送 + 線上視頻實(shí)時(shí)直播 + 會(huì)后資料推送
報(bào)名方式:https://www.zaozao.run/conf/c63
大會(huì)主辦方:前端早早聊
