2022 年 6 月 3 日

使用 NestJS 和 Prisma 構建 REST API

閱讀約 20 分鐘

NestJS 是一個卓越的 Node.js 框架,最近獲得了開發者的廣泛喜愛和關注。本文將教您如何使用 NestJS、Prisma、PostgreSQL 和 Swagger 構建後端 REST API。

Building a REST API with NestJS and Prisma

目錄

簡介

在本教程中,您將學習如何為一個名為“Median”(一個簡單的 Medium 克隆)的部落格應用構建後端 REST API。您將從建立一個新的 NestJS 專案開始。然後啟動您自己的 PostgreSQL 伺服器,並使用 Prisma 連線到它。最後,您將構建 REST API 並使用 Swagger 對其進行文件化。

The final application

您將使用的技術

您將使用以下工具來構建此應用程式

先決條件

假定知識

這是一個對初學者友好的教程。但是,本教程假定您具有以下知識:

  • JavaScript 或 TypeScript 的基本知識(首選)
  • NestJS 的基本知識

注意:如果您不熟悉 NestJS,可以透過遵循概述部分在 NestJS 文件中快速學習基礎知識。

開發環境

要遵循本教程,您需要:

注意 1:可選的 Prisma VSCode 擴充套件為 Prisma 提供了非常出色的智慧感知和語法高亮。

注意 2:如果您沒有 Unix shell(例如,您在 Windows 機器上),您仍然可以遵循本教程,但 shell 命令可能需要根據您的機器進行修改。

生成 NestJS 專案

您首先需要安裝 NestJS CLI。NestJS CLI 在處理 NestJS 專案時非常方便。它內建了實用工具,可幫助您初始化、開發和維護 NestJS 應用程式。

您可以使用 NestJS CLI 建立一個空專案。首先,在您希望專案所在的目錄中執行以下命令:

CLI 將提示您為專案選擇一個包管理器——請選擇 npm。之後,您應該在當前目錄中擁有一個新的 NestJS 專案。

在您首選的程式碼編輯器中開啟專案(我們推薦 VSCode)。您應該看到以下檔案:

您大部分的程式碼都將位於 src 目錄中。NestJS CLI 已經為您建立了一些檔案。其中一些值得注意的檔案是:

  • src/app.module.ts:應用程式的根模組。
  • src/app.controller.ts:一個基本的控制器,包含一個路由:/。此路由將返回一個簡單的 'Hello World!' 訊息。
  • src/main.ts:應用程式的入口點。它將啟動 NestJS 應用程式。

您可以使用以下命令啟動您的專案:

此命令將監視您的檔案,並在您進行更改時自動重新編譯和重新載入伺服器。要驗證伺服器是否正在執行,請訪問 URL https://:3000/。您應該會看到一個空白頁面,上面顯示著訊息 'Hello World!'

注意:在您學習本教程時,應保持伺服器在後臺執行。

建立 PostgreSQL 例項

您將使用 PostgreSQL 作為 NestJS 應用程式的資料庫。本教程將向您展示如何透過 Docker 容器在您的機器上安裝和執行 PostgreSQL。

注意:如果您不想使用 Docker,您可以原生設定 PostgreSQL 例項或在Heroku 上獲取託管的 PostgreSQL 資料庫

首先,在專案主資料夾中建立一個 docker-compose.yml 檔案:

這個 docker-compose.yml 檔案是一個配置檔案,它將包含執行一個內建 PostgreSQL 的 Docker 容器的規範。在檔案中建立以下配置:

關於此配置,有幾點需要理解:

  • The image 選項定義要使用的 Docker 映象。這裡,您正在使用 postgres 映象版本 13.5。
  • The environment 選項指定在初始化期間傳遞給容器的環境變數。您可以在此處定義容器將使用的配置選項和秘密——例如使用者名稱和密碼。
  • The volumes 選項用於在主機檔案系統中持久化資料。
  • The ports 選項將主機機器的埠對映到容器。格式遵循 'host_port:container_port' 約定。在這種情況下,您將主機機器的埠 5432 對映到 postgres 容器的埠 54325432 通常是 PostgreSQL 使用的埠。

確保您的機器的埠 5432 上沒有執行任何其他程式。要啟動 postgres 容器,請開啟一個新的終端視窗並在專案主資料夾中執行以下命令:

如果一切正常,新的終端視窗應該顯示資料庫系統已準備好接受連線的日誌。您應該在終端視窗中看到與以下內容相似的日誌:

恭喜 🎉!您現在擁有了自己的 PostgreSQL 資料庫可以操作了!

注意:如果您關閉終端視窗,它也將停止容器。您可以透過在命令末尾新增 -d 選項來避免這種情況,例如:docker-compose up -d。這將在後臺無限期地執行容器。

設定 Prisma

資料庫已準備就緒,現在是時候設定 Prisma 了!

初始化 Prisma

首先,安裝 Prisma CLI 作為開發依賴。Prisma CLI 將允許您執行各種命令並與您的專案進行互動。

您可以透過執行以下命令在專案中初始化 Prisma:

這將建立一個新的 prisma 目錄,其中包含 schema.prisma 檔案。這是包含您資料庫模式的主要配置檔案。此命令還會在您的專案中建立一個 .env 檔案。

設定您的環境變數

.env 檔案中,您應該會看到一個 DATABASE_URL 環境變數,其中包含一個虛擬連線字串。將其替換為您的 PostgreSQL 例項的連線字串。

注意:如果您沒有使用 Docker(如上一節所示)來建立 PostgreSQL 資料庫,您的連線字串將與上面所示的不同。PostgreSQL 的連線字串格式可在Prisma 文件中找到。

理解 Prisma 模式

如果您開啟 prisma/schema.prisma,您應該會看到以下預設模式:

此檔案採用 Prisma 模式語言編寫,Prisma 使用該語言定義您的資料庫模式。schema.prisma 檔案包含三個主要組成部分:

  • 資料來源:指定您的資料庫連線。上述配置意味著您的資料庫提供者是 PostgreSQL,並且資料庫連線字串可在 DATABASE_URL 環境變數中找到。
  • 生成器:指示您要生成 Prisma Client,這是一個用於您資料庫的型別安全查詢構建器。它用於向資料庫傳送查詢。
  • 資料模型:定義您的資料庫模型。每個模型都將對映到底層資料庫中的一個表。目前您的模式中沒有模型,您將在下一節中探討這部分內容。

注意:有關 Prisma 模式的更多資訊,請查閱Prisma 文件

資料建模

現在是時候為您的應用程式定義資料模型了。對於本教程,您只需要一個 Article 模型來表示部落格上的每篇文章。

prisma/prisma.schema 檔案中,為您的模式新增一個名為 Article 的新模型:

這裡,您建立了一個包含多個欄位的 Article 模型。每個欄位都有一個名稱(idtitle 等)、一個型別(IntString 等)以及其他可選屬性(@id@unique 等)。透過在欄位型別後新增 ? 可以使欄位變為可選。

The id 欄位有一個名為 @id 的特殊屬性。此屬性表示該欄位是模型的主鍵。@default(autoincrement()) 屬性表示該欄位應自動遞增並分配給任何新建立的記錄。

The published 欄位是一個標誌,用於指示文章是已釋出還是處於草稿模式。@default(false) 屬性表示此欄位預設應設定為 false

兩個 DateTime 欄位 createdAtupdatedAt 將跟蹤文章的建立時間和上次更新時間。@updatedAt 屬性將在文章修改時自動用當前時間戳更新該欄位。

遷移資料庫

定義好 Prisma 模式後,您將執行遷移以在資料庫中建立實際的表。要生成並執行您的第一次遷移,請在終端中執行以下命令:

此命令將執行三件事:

  1. 儲存遷移:Prisma Migrate 將對您的模式進行快照,並計算出執行遷移所需的 SQL 命令。Prisma 會將包含 SQL 命令的遷移檔案儲存到新建立的 prisma/migrations 資料夾中。
  2. 執行遷移:Prisma Migrate 將執行遷移檔案中的 SQL,以在您的資料庫中建立底層表。
  3. 生成 Prisma Client:Prisma 將根據您最新的模式生成 Prisma Client。由於您尚未安裝 Client 庫,CLI 也會為您安裝。您應該會在 package.json 檔案的 dependencies 中看到 @prisma/client 包。Prisma Client 是一個從您的 Prisma 模式自動生成的 TypeScript 查詢構建器。它針對您的 Prisma 模式進行了定製,並將用於向資料庫傳送查詢。

注意:您可以在Prisma 文件中瞭解更多關於 Prisma Migrate 的資訊。

如果成功完成,您應該會看到類似以下的訊息:

檢查生成的遷移檔案,以瞭解 Prisma Migrate 在幕後做了什麼:

注意:您的遷移檔名會略有不同。

這是在您的 PostgreSQL 資料庫中建立 Article 表所需的 SQL。它是 Prisma 根據您的 Prisma 模式自動生成和執行的。

填充資料庫

當前資料庫是空的。因此,您將建立一個種子指令碼,用一些虛擬資料填充資料庫。

首先,建立一個名為 prisma/seed.ts 的種子檔案。此檔案將包含填充資料庫所需的虛擬資料和查詢。

然後,在種子檔案中新增以下程式碼:

在此指令碼中,您首先初始化 Prisma Client。然後,您使用 prisma.upsert() 函式建立兩篇文章。upsert 函式僅在沒有文章與 where 條件匹配時才建立新文章。您使用 upsert 查詢而不是 create 查詢,因為 upsert 可以避免意外嘗試插入相同記錄兩次所產生的錯誤。

您需要告訴 Prisma 在執行種子命令時執行哪個指令碼。您可以透過將 prisma.seed 鍵新增到 package.json 檔案末尾來完成此操作:

The seed 命令將執行您之前定義的 prisma/seed.ts 指令碼。此命令應該自動工作,因為 ts-node 已作為開發依賴安裝在您的 package.json 中。

使用以下命令執行資料填充:

您應該會看到以下輸出:

注意:您可以在Prisma 文件中瞭解更多關於資料填充的資訊。

建立 Prisma 服務

在您的 NestJS 應用程式中,最佳實踐是將 Prisma Client API 從應用程式中抽象出來。為此,您將建立一個包含 Prisma Client 的新服務。這個名為 PrismaService 的服務將負責例項化 PrismaClient 例項並連線到您的資料庫。

Nest CLI 提供了一種從命令列直接生成模組和服務的方式。在您的終端中執行以下命令:

注意 1:如有必要,請參考 NestJS 文件中關於服務模組的介紹。

注意 2:在某些情況下,當伺服器已在執行時執行 nest generate 命令,可能導致 NestJS 丟擲異常,提示:Error: Cannot find module './app.controller'。如果您遇到此錯誤,請在終端中執行以下命令:rm -rf dist,然後重新啟動伺服器。

這應該會生成一個新的子目錄 ./src/prisma,其中包含 prisma.module.tsprisma.service.ts 檔案。服務檔案應包含以下程式碼:

Prisma 模組將負責建立 單例例項的 PrismaService,並允許在整個應用程式中共享該服務。為此,您將把 PrismaService 新增到 prisma.module.ts 檔案中的 exports 陣列:

現在,任何匯入 PrismaModule 的模組都將可以訪問 PrismaService,並可以將其注入到自己的元件/服務中。這是 NestJS 應用程式的常見模式。

至此,您已完成 Prisma 的設定!現在您可以開始構建 REST API 了。

設定 Swagger

Swagger 是一個使用OpenAPI 規範來文件化您的 API 的工具。Nest 有一個專門用於 Swagger 的模組,您將很快使用它。

首先安裝所需的依賴:

現在開啟 main.ts 並使用 SwaggerModule 類初始化 Swagger:

應用程式執行時,開啟瀏覽器並導航到 https://:3000/api。您應該會看到 Swagger UI。

Swagger User Interface

Article 模型實現 CRUD 操作

在本節中,您將為 Article 模型實現建立、讀取、更新和刪除 (CRUD) 操作以及任何附帶的業務邏輯。

生成 REST 資源

在實現 REST API 之前,您需要為 Article 模型生成 REST 資源。這可以使用 Nest CLI 快速完成。在您的終端中執行以下命令:

您會收到一些 CLI 提示。請相應地回答問題:

  1. What name would you like to use for this resource (plural, e.g., "users")? articles
  2. What transport layer do you use? REST API
  3. Would you like to generate CRUD entry points? Yes

您現在應該會找到一個新的 src/articles 目錄,其中包含您的 REST 端點的所有樣板程式碼。在 src/articles/articles.controller.ts 檔案中,您將看到不同路由(也稱為路由處理程式)的定義。處理每個請求的業務邏輯封裝在 src/articles/articles.service.ts 檔案中。目前,此檔案包含模擬實現。

如果您再次開啟 Swagger API 頁面,您應該會看到類似以下內容:

Auto-generated "articles" endpoints

The SwaggerModule 會搜尋路由處理程式上的所有 @Body()@Query()@Param() 裝飾器來生成此 API 頁面。

PrismaClient 新增到 Articles 模組

要在 Articles 模組中訪問 PrismaClient,您必須將 PrismaModule 作為匯入新增。將以下 imports 新增到 ArticlesModule

現在,您可以在 ArticlesService 內部注入 PrismaService 並使用它來訪問資料庫。為此,請在 articles.service.ts 中新增一個建構函式,如下所示:

定義 GET /articles 端點

此端點的控制器名為 findAll。此端點將返回資料庫中所有已釋出的文章。findAll 控制器如下所示:

您需要更新 ArticlesService.findAll() 以返回資料庫中所有已釋出文章的陣列:

The findMany 查詢將返回所有符合 where 條件的 article 記錄。

您可以透過訪問 https://:3000/api 並點選 GET/articles 下拉選單來測試該端點。點選 Try it out (嘗試一下),然後點選 Execute (執行) 以檢視結果。

注意:您也可以直接在瀏覽器中或透過 REST 客戶端(例如 Postman)執行所有請求。Swagger 還會為每個請求生成 curl 命令,以防您想在終端中執行 HTTP 請求。

定義 GET /articles/drafts 端點

您將定義一個新的路由來獲取所有未釋出的文章。NestJS 沒有自動為該端點生成控制器路由處理程式,因此您必須自己編寫。

您的編輯器應該會顯示一個錯誤,提示不存在名為 articlesService.findDrafts() 的函式。要解決此問題,請在 ArticlesService 中實現 findDrafts 方法:

The GET /articles/drafts 端點現在將在 Swagger API 頁面中可用。

注意:我建議您在完成每個端點實現後,透過 Swagger API 頁面對其進行測試。

定義 GET /articles/:id 端點

此端點的控制器路由處理程式名為 findOne。它看起來像這樣:

該路由接受一個動態的 id 引數,該引數被傳遞給 findOne 控制器路由處理程式。由於 Article 模型有一個整數 id 欄位,因此需要使用 + 運算子將 id 引數轉換為數字。

現在,更新 ArticlesService 中的 findOne 方法,以返回具有給定 ID 的文章:

再次,透過訪問 https://:3000/api 來測試該端點。點選 GET /articles/{id} 下拉選單。點選 Try it out (嘗試一下),為 id 引數新增一個有效值,然後點選 Execute (執行) 以檢視結果。

定義 POST /articles 端點

這是用於建立新文章的端點。此端點的控制器路由處理程式名為 create。它看起來像這樣:

注意,它期望請求正文中包含 CreateArticleDto 型別的引數。DTO(資料傳輸物件)是一個定義資料如何透過網路傳送的物件。目前,CreateArticleDto 是一個空類。您將向其中新增屬性以定義請求正文的形狀。

The @ApiProperty 裝飾器是必需的,以使類屬性對 SwaggerModule 可見。有關此內容的更多資訊可在NestJS 文件中找到。

The CreateArticleDto 現在應該在 Swagger API 頁面中的 Schemas (模式) 下定義。UpdateArticleDto 的形狀是根據 CreateArticleDto 的定義自動推斷的。因此,UpdateArticleDto 也在 Swagger 中定義。

現在更新 ArticlesService 中的 create 方法,以在資料庫中建立新文章:

定義 PATCH /articles/:id 端點

此端點用於更新現有文章。此端點的路由處理程式名為 update。它看起來像這樣:

The updateArticleDto 的定義被定義為 CreateArticleDtoPartialType。因此,它可以擁有 CreateArticleDto 的所有屬性。

就像之前一樣,您必須更新此操作對應的服務方法:

The article.update 操作將嘗試查詢具有給定 idArticle 記錄,並使用 updateArticleDto 的資料對其進行更新。

如果在資料庫中未找到此類 Article 記錄,Prisma 將返回錯誤。在這種情況下,API 不會返回使用者友好的錯誤訊息。您將在未來的教程中學習 NestJS 的錯誤處理。

定義 DELETE /articles/:id 端點

此端點用於刪除現有文章。此端點的路由處理程式名為 remove。它看起來像這樣:

就像之前一樣,前往 ArticlesService 並更新相應的方法:

這是 articles 端點的最後一個操作。恭喜,您的 API 幾乎準備就緒!🎉

在 Swagger 中分組端點

ArticlesController 類新增 @ApiTags 裝飾器,以便在 Swagger 中將所有 articles 端點分組在一起:

The API 頁面現在將 articles 端點分組在一起。

更新 Swagger 響應型別

如果您檢視 Swagger 中每個端點下的響應 (Responses) 選項卡,您會發現描述 (Description) 是空的。這是因為 Swagger 不知道任何端點的響應型別。您將使用一些裝飾器來解決這個問題。

首先,您需要定義一個實體,Swagger 可以用它來識別返回的 entity 物件的形狀。為此,請按如下方式更新 articles.entity.ts 檔案中的 ArticleEntity 類:

這是 Prisma Client 生成的 Article 型別的一個實現,為每個屬性添加了 @ApiProperty 裝飾器。

現在,是時候使用正確的響應型別來標註控制器路由處理程式了。NestJS 為此提供了一組裝飾器。

您為 GETPATCHDELETE 端點添加了 @ApiOkResponse,併為 POST 端點添加了 @ApiCreatedResponsetype 屬性用於指定返回型別。您可以在NestJS 文件中找到 NestJS 提供的所有響應裝飾器。

現在,Swagger 應該在 API 頁面上正確定義所有端點的響應型別。

總結與最終說明

恭喜!您已經使用 NestJS 構建了一個基本的 REST API。在本教程中,您:

  • 使用 NestJS 構建了 REST API
  • 在 NestJS 專案中順利集成了 Prisma
  • 使用 Swagger 和 OpenAPI 文件化了您的 REST API

本教程的主要收穫之一是使用 NestJS 和 Prisma 構建 REST API 是多麼容易。這是一個極具生產力的技術棧,可用於快速構建結構良好、型別安全且易於維護的後端應用程式。

您可以在 GitHub 上找到此專案的原始碼。如果您發現問題,請隨時在倉庫中提出問題或提交 PR。您也可以直接在 Twitter 上聯絡我。

不要錯過下一篇文章!

訂閱 Prisma 郵件列表

© . This site is unofficial and not affiliated with Prisma Data, Inc.