动态

详情 返回 返回

【Node 連載 3/9】如何高效開發表現層 Node.js 應用 - 动态 详情

本文是 2021 年 12 月 26 日,第三十五屆 - 前端早早聊【前端搞 Node.js】專場,來自預測科技的技術總監 —— 芋頭的分享。感謝 AI 的發展,藉助 GPT 的能力,最近我們終於可以非常高效地將各位講師的精彩分享文本化後,分享給大家。(完整版含演示請看錄播視頻):https://www.zaozao.run/video/c35

完整版高清 PPT 請添加小助手「zzleva」獲取

正文如下:

大家好,我是芋頭。今天分享的主題是《如何高效開發表現層 Node.js 應用》,涉及到表現層應用的開發。我今天分享的方案是針對簡單場景的,旨在讓前端開發人員不必掌握太多關於 Node.js 的背景知識和專業知識,即使沒有代碼編寫經驗,也能完成一些簡單的服務端開發任務。

有很多小公司都面臨着這樣的情況:大公司的開發人員更喜歡專注於專業,需要掌握 ORM、服務器和運維等專業知識,但在小公司,我們可以直接上手完成開發任務。因此,我的方案是介紹一系列工具的組合,這些工具是我在創業公司裏的一些嘗試,我想簡單地和大家分享一下。

這裏展示的就是我今天想講的全部內容。

我的方案的底層用的是 Nest.js 開發框架,實際上,使用什麼框架不是很重要,因為它只提供最基本的功能,這些功能是所有框架都具有的。Nest.js 封裝了網絡層和應用分層,在國外使用較多,因為它的開發方式與 Spring Boot 類似,所以對於熟悉 Java 的人來説很熟悉。

上層使用的是名為 Prisma 的 ORM,這是一個比較特殊的 ORM,是一個比較純粹的 ORM,是 Model 層的 ORM。我今天的整個方案是通過 Prisma 將所有開發流程串聯起來,將多個工具連接在一起。基本上,你只需要寫很少的代碼,就可以完成從底層數據層到頂層控制層再到整個 API 文檔的應用開發方案。

我最近比較關注的是這種漸進增強的方案,很多國外社區的方案都注重這一點。也就是説,這些方案都是可插拔的,包括我今天講的內容,你可以選擇使用或者不使用,或者只使用其中的一部分,而不會影響其他開發工作。所以,如果你的公司還比較小,而且想讓前端負責這部分工作,你可以讓他們簡單地使用這些工具。但是當你的公司或者團隊發展之後,你可能需要專業的 Node.js 開發人員,他們瞭解 ORM、數據庫、緩存等知識。這時你可以卸載上層的工具,使用 Nest.js,甚至將 Nest.js 替換成其他框架。這是一種靈活的思路,上層工具的耦合度很低,是可替換的。

這是我今天要介紹的三個核心工具, Nest.js,Prisma,GraphQL。這三者都非常重要。

Prisma

首先簡單介紹一下 Prisma,Prisma 在官網上的介紹是它是「Next-generation Node.js and TypeScript ORM」,強調了它對類型的強大支持。

關於 Prisma 的使用,實際上它是一個 ORM 工具,但它的重點並不是讓你自己去定義模型、類等等,而是在進入使用時首先要定義一個描述文件。這個描述文件與熟悉 GraphQL 的同學應該都知道,GraphQL 有兩種開發方式,一種是架構優先,一種是代碼優先。而 Prisma 作為一個整套方案,在國外與 GraphQL 有着緊密的聯繫,但它們之間的耦合並不是特別嚴格,而是比較輕鬆的。

因此,在 Prisma 的開發中,首先要定義一個描述文件,其中包括

  • 定義 Model(數據模型),這是每個 ORM 都需要做的事情。
  • 定義 DataSource(數據源),這是因為在 Prisma 中,它接管了與數據庫的連接,這一層被包含在整個庫中,所以你不需要關注如何連接數據庫,而是由 Prisma 內部進行數據庫連接的管理。
  • 定義 Generator(生成器),是 Prisma 的核心重點。它定義了數據模型和數據連接管理方式。Generator 其實是在定義一些官方和第三方的生成工具,它可以生成強類型的 GraphQL schema 的 JS 包,生成類型的 DTO 的定義,還有 GraphQL Schema 的定義。

所以在使用 Prisma 時,你可能會發現它的用法有些奇怪。通常情況下,當你使用其他庫時,你只需要直接引用 Node models 中的 ORM 的代碼。但是,Prisma 中 Node models 的代碼是在運行時動態生成的,它並不是在你下載下來的時候就已經存在的。所以,在使用 Prisma 時,實際上你並沒有去定義像我剛才在右邊代碼中寫的「this.prisma.tag」這樣的模型,但是你卻可以將它當作一個正常的對象來使用。

當然,Prisma 還有很多其他的特性,我剛才只列舉了一部分。

Prisma 提供了一套數據訂正的工具。實際上很多 ORM 都已經在使用這種特性了。但是在一些小型公司或者小型產品中,使用這個工具其實非常方便。然而,在一些大型團隊中,使用這種數據訂正工具可能會比較困難。不過,在這方面,Prisma 在這塊的功能實際上做得比較成熟。

當你需要修改一個 Model 中的任何信息時,它都會幫你生成訂正的語句,而且會進行預測,例如,如果你之前刪除了一個字段,而這個字段裏面有信息,並且是必填的,而你現在把它改成了非必填,那麼這時候系統會生成一條非常複雜的訂正語句,它會進行預測並讓你做出選擇。

因此,在某些小型場景中,這個數據訂正工具非常方便。最後,它會生成一個訂正的記錄,並可以將這個訂正記錄同步到你的 Git 中。關於在生產環境中是否需要使用這套工具,這裏我就不詳細展開了。

Nest.js

接下來是關於 Nest.js 框架的簡單介紹。這個框架實際上與許多其他框架非常類似,它實現了一些分層和全局鈎子等機制,用於串聯各種開發工具。我想要在 Nest.js 中儘量減少編寫邏輯的數量。

因此,大家最終可以看到,在 Nest.js 端實現了一個完整的增刪改查操作,甚至包括複雜的操作,基本上沒有代碼。因此,我只會簡單地介紹一下這個框架是什麼樣的。

Nest.js 框架實際上有三個核心概念:Controller,Provider,Moduler。

最底層、最重要的是 Moduler,它是用來組織一個完整業務邏輯中的所有模塊的總入口。每個 Moduler 都有一個完整的分層,入口是 Controller,所有的請求都首先進入 Controller 層。Provider 即服務層,主要提供一些類似於平常編寫的 service 層邏輯的功能。

大家可以看到,這裏沒有明確的提到 Model 層。雖然在模型層中有一些定義,但今天我們使用的是 Prisma,它基本上佔用了 Model 層。因此,開發一個 Nest.js 的流程實際上是一個正常的開發流程,即當應用進來時,需要寫 controller,service,定義 DTO 和 VO。

一旦有了 Prisma 之後,我們可以使用 Prisma 生成一個 ORM 的庫 ,同時它還會幫助我們生成一些 DTO 和 VO 的代碼,所以我們只要關注 controller 和 service。此外,Nest.js 還提供了一個用於增加、刪除、修改、查詢的一個腳手架。當執行了這個命令後,它會為你生成一個類似於下面這個圖中的文件結構,然後你只需要填充其中的 controller 和 service 即可。

大家看似簡單,只是生成了一個 Controller,然後手寫了一些代碼,但其實這些是非常初級的,不能幫你生成邏輯代碼。所以要自己去定義參數的類型,調用 Service 層。當然,這個 Controller 層的代碼還是比較簡單的,不需要做太多事情,邏輯都在 Service 層。在這裏需要寫增刪改查的邏輯,例如基本的分頁和條件過濾等技術,以及聯合查詢等。

我不希望前端變成一個服務端的開發者。前端寫服務端的意義是什麼呢?尤其對於一個小團隊來説,為什麼要這麼做呢?其實我覺得這個框架的初衷並不是讓前端變成一個服務端的開發者,而且這樣做可能會遭到後端開發者的質疑,我用 Spring Boot 寫得很好,為什麼要用 Nest.js?它有什麼本質上的改變嗎?其實並沒有。

所以為什麼要讓前端嘗試去做一些服務端的開發呢?其實我初衷是為了提高效率,面對一個小的普通界面上的更改,避免前端設計師、前端開發人員和服務端開發人員都要參與才能完成此次更改,從而減少效率損耗。我希望前端儘量少碰服務端的代碼,但同時又能靈活地調整接口,以適應前端的不斷變更。所以我不希望大家去寫類似於 find、where、select、count 等這樣的代碼,因為對於不太熟悉這些概念的前端來説,很容易寫出問題來,而且我也不希望花太多精力去監督他們如何寫這些東西。

接下來我會繼續詳細介紹。這裏,大家可以看到 Prisma 類型是非常強的,不僅僅是模型的類型,還包括模型生成的操作類的類型,以及各種查詢條件的類型,非常精細。實際上,所有使用的東西的類型都是強類型,都是根據模型定義推導出來的。因此,在它的定義中,它標明瞭它是一個 TypeScript 的 ORM,這是它的優勢之一。

如果你要使用 Nest.js,不管你使用什麼框架,你都要做一些其他的事情,比如身份驗證、異常處理、請求返回的統一封裝和配置等。

網上有一些資料,但有時候描述得不是很完整。因此,我做了一個示例,如果大家需要使用的話,可以從我的 GitHub 項目中 clone 出來,這樣可以避免很多坑。

我們剛剛完成了一個正常的 Nest.js 和 Prisma 的應用程序開發,實際上代碼量不多,對於我這種之前做過 Node.js 服務端開發的人來説沒有太大的壓力。但是對於一個前端開發者來説,我向他介紹這些東西,他可能會有些難以接受,那是否可以在不編寫代碼的情況下完成複雜的查詢、插入、更新和刪除邏輯,並且能夠靈活調整前端界面?

GraphQL

要解決上面的問題,我們可以引入 GraphQL,這實際上是一種面向前端的接口開放方式。GraphQL 並不是一個萬能藥,不是説通過 GraphQL 提供的接口就變得高級了,或者解決了很多問題。實際上,它只適用於某些場景,特別是它最初設計的樹狀數據結構,而不是圖狀數據結構,可能在樹狀數據結構的場景下更加適合。

但是今天我的話題核心實際上是 Prisma 和 GraphQL 的配合。雖然 Prisma 的官方定義中沒有提到這一點,但是如果你查看官方文檔,它有一個關於 Prisma 和 GraphQL 如何一起使用的話題。

在這裏我使用了兩個工具。

prisma-nestjs-graphql

第一個工具是我要用 Prisma 的 Schema 生成 GraphQL 的 Schema,這個工具實在不斷演進。我使用的是它之前的一個版本,prisma-nestjs-graphql,這個庫的名字非常直白,就是將這三者連接起來,通過執行 Prisma 的 generate 命令,可以在項目中生成內容。生成的內容不僅包括 Model 的定義(例如 tags),還會生成一些定義這些操作的參數。

我的查詢操作非常靈活,包括了 where 條件、in、order by、group by、count、分頁 等操作,而這個工具可以生成這些操作所需的文件。當我生成了這些文件並引入了這個插件後,我的代碼就發生了變化。

這裏 Resolver 實際上是用於 GraphQL 開發的,類似於 Controller 的一個分層,實際上 Resolver 內的邏輯沒有發生太大的變化。雖然它與 Controller 的定義有些類似,但是到了 Service 層,你會發現一個神奇的事情,它內部沒有代碼。在正常的開發中,Service 層通常需要編寫調用 ORM 的代碼,但在這裏,基本上是透傳的,你可以直接透傳 update、delete、count 等操作。當然,在這裏你也可以編寫正常的查詢操作,但在普通情況下,你只需要透傳這些操作 Resolver,Resolver 會直接透傳給前端。

swagger-to-graphql

在實際開發過程中,前端代碼還需要訪問服務端之前開發的接口。為了使這些接口與前端代碼的數據訪問更好地結合,需要使用 swagger-to-graphql 工具,它可以將 Swagger 的格式(即 Open API 的標準格式)轉換為 Graphql 數據模型,使得前端代碼可以直接訪問服務端的接口。這個庫的使用非常簡單,只需傳入一個 Swagger 文檔,定義一個 Provider,把 Swagger 的數據傳進來,就會自動生成一個透傳的 GraphQL 接口供使用。可以在 GraphQL 的視圖中看到服務端定義的接口以及所有可用的查詢參數。

最後

以上就是我的全部分享,我介紹了最基本的三個東西,即 Nest.js、Prisma 和 GraphQL,以及我用到的一些工具。

我沒有深入講解 Prisma 框架內部如何實現之前在 Service 裏面一行代碼都不寫的狀態,這是因為 Prisma 本身可以自適應到 GraphQL 的 Resolver 上面去,這是普通的 ORM 庫難以實現的。另外,今天我將的內容都可以在我的GitHub 倉庫中找到。如果你對這個話題感興趣,可以去找找我的倉庫,裏面包括了一些鑑權、錯誤處理和返回結果的封裝,還有關於 Nest.js 的一些東西,非常方便用於小型應用的開發。


user avatar alibabawenyujishu 头像 haoqidewukong 头像 freeman_tian 头像 chongdianqishi 头像 razyliang 头像 huajianketang 头像 huichangkudelingdai 头像 febobo 头像 imba97 头像 zhulongxu 头像 wmbuke 头像 munergs 头像
点赞 89 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.