如何使用 Fastify 構建 REST API?


Fastify 是一個主要用於 JavaScript 後端開發的框架。它是您可以使用的最輕量級的後端框架之一,也是在想要避免使用 Express 和 Hapi 等較重的 Node 框架時首選它的主要原因之一。

自誕生以來,Fastify 已釋出了多個版本。在最新版本中,我們甚至能夠驗證傳入和傳出的請求以及請求引數。毫不奇怪,開發 Fastify 的人員聲稱它是您可以使用的最快的 Node.js 框架,與 Koa、Hapi 和 Express 等其他框架相比。

Fastify 框架之所以廣受歡迎,主要是因為其輕量級的設計。Fastify 與原生框架的不同之處在於,它將所有內容都視為外掛,而在 JavaScript 中,我們將所有內容都視為物件。這反過來又使我們能夠快速將專案的特定功能封裝為外掛,並將其分發到其他專案中。

在本教程中,我們將透過示例學習 Fastify 框架的以下方面

  • 如何建立一個簡單的 Fastify 伺服器

  • 如何在 Fastify 中定義 API 路由

  • 如何為我們的請求新增模式驗證

  • 如何定義鉤子

需求和安裝

使用 Fastify 構建 REST API 的第一步是在專案中建立專案並安裝 Fastify 作為依賴項。

您首先需要以下內容:

  • 最新版本的 Node.js

  • 一個用於測試端點的工具,例如 PostMan 或 cURL。

要驗證 Node 版本,您可以執行下面顯示的命令。

node -v

執行上述命令後,您將在終端上獲得以下**輸出**。

v16.16.0

如果您以某種方式沒有獲得相同的輸出,則需要首先在本地機器上安裝“node”,然後繼續執行下面顯示的命令。

下一步是建立一個空的 Node 專案。如果您還沒有專案,則可以使用下面顯示的命令來初始化它:

npm init -y

在終端中執行上述命令後,將建立一個“package.json”檔案,該檔案將跟蹤所有依賴項以及您可能需要的指令碼。

為了能夠使用 Fastify 框架,我們需要將其匯入為依賴項,我們可以使用下面顯示的命令來實現:

npm i fastify --save

這樣做後,我們應該在“package.json”檔案中的依賴項中看到 Fastify。

現在我們已經準備好進入構建 REST API 的不同階段。

如何建立簡單的 Fastify 伺服器?

要建立簡單的 Fastify 伺服器,我們首先需要建立一個 JS 檔案。假設我們要將檔案命名為“index.js”。要建立同一個檔案,請在終端中執行以下命令:

touch index.js

index.js

現在在您喜歡的程式碼編輯器中開啟您的專案,並在“index.js”檔案中編寫以下程式碼。

// to require the framework
const app = require('fastify')({
   logger: true
})

// to declare a single route
app.get('/', function (req, reply) {
   reply.send({
      Welcome: 'TutorialsPoint'
   })
})

// Run the server!
app.listen({ port: 3000 }, (err, address) => {
   if (err) {
      app.log.error(err)
      process.exit(1)
   }
   app.log.info(`The server is listening on ${address}`)
})

在上面的示例中,我載入了 Fastify 應用程式物件,然後在該物件中,我啟用了日誌記錄。稍後,我聲明瞭一個僅回覆單個響應的單個路由,即“Welcome: TutorialsPoint”。最後一個程式碼塊描述了我們在埠 3000 上偵聽的情況。

要執行上述程式碼,我們需要在終端中執行以下命令。

node index.js

執行上述命令後,我們的伺服器將在以下端點上啟動並執行。

https://:3000

現在要測試它,我們可以使用 PostMan 或 cURL,或者簡單地訪問瀏覽器,因為它是一個簡單的 GET 請求。

我將使用 cURL 命令。請考慮下面顯示的命令:

curl https://:3000 

執行上述命令後,您將在終端上獲得以下**輸出**。

{"Welcome":"TutorialsPoint"}

建立簡單的 Fastify 伺服器的第一步已完成,現在讓我們學習如何在 API 中定義路由。

如何在 Fastify 中定義 API 路由?

如果 API 中沒有多個路由,那麼它就沒有意義。在我們的 API 中,我們將有多個路由,因為我們的 REST API 是關於獲取不同書籍及其作者和標題的詳細資訊。

在我們的 REST API 示例中,我們將定義以下路由。

  • **GET** – 所有書籍位於 /api/books

  • **GET** – 單本書位於 /api/book/:id

  • **POST** – 在 /api/books 新增一本書

  • **PUT** – 在 /api/books/:id 更新一本書

  • **DELETE** – 在 /api/delete/:id 刪除一本書

在定義路由之前,有必要為這些路由定義控制器。

建立書籍控制器

為了使我們的程式碼模組化和簡潔,讓我們建立一個名為 controller 的目錄,並在該目錄中建立一個名為 books.js 的檔案。

books.js

在 books.js 檔案中,貼上下面顯示的程式碼:

let books = [{
   id: 1,
   title: 'Maharana Pratap : The Invincible Warrior',
   author: 'Rima Hooja'
},
{
   id: 2,
   title: 'Prithviraj Chauhan - A Light on the Mist in History',
   author: 'Virendra Singh Rathore'
},
{
   id: 3,
   title: 'Rani Laxmibai: Warrior-Queen of Jhansi',
   author: 'Pratibha Ranade'
}
]

// Handlers
const getAllBooks = async (req, reply) => {  
}
const getBook = async (req, reply) => {
   const id = Number(req.params.id) 
   const book = books.find(book => book.id === id)
   return book
}
const addBook = async (req, reply) => {
   const id = books.length + 1
   const newBook = {
      id,
      title: req.body.title,
      author: req.body.author
   }
   books.push(newBook)
   return newBook
}

const updateBook = async (req, reply) => {
   const id = Number(req.params.id)
   books = books.map(book => {
      if (book.id === id) {
         return {
            id,
            title: req.body.title,
            author: req.body.author
         }
      }
   })
   return {
      id,
      title: req.body.title
   }
}

const deleteBook = async (req, reply) => {
   const id = Number(req.params.id)

   books = books.filter(book => book.id !== id)
   return {
      msg: `Blog with ID ${id} is deleted`
   }
}
module.exports = {
   getAllBooks,
   getBook,
   addBook,
   updateBook,
   deleteBook
}

在上面的程式碼中,定義了不同的處理程式。這些是:

  • **getAllBooks** – 獲取包含所有書籍的響應

  • **getBook** – 從書籍 ID 獲取特定書籍。

  • **addBook** – 將書籍新增到書籍物件的書籍陣列中。

  • **updateBook** – 更新書籍

  • **deleteBook** – 從書籍物件陣列中刪除書籍。

需要注意的是,為了節省時間並使控制器保持簡單,我使用物件陣列來儲存書籍資訊,而不是使用資料庫。

下一步是建立路由,以便我們可以在其中使用這些控制器函式。要建立路由,我們將遵循類似的資料夾結構。

讓我們在專案的根目錄中建立一個名為“routes”的目錄。在“routes”目錄中,讓我們建立一個名為“books.js”的檔案。

books.js

現在,將以下程式碼貼上到“books.js”檔案中。

const booksController = require('../controller/books');

const routes = [{
method: 'GET',
   url: '/api/books',
   handler: booksController.getAllBooks
},
{
   method: 'GET',
   url: '/api/books/:id',
   handler: booksController.getBook
},
{
   method: 'POST',
   url: '/api/books',
   handler: booksController.addBook
},
{
   method: 'PUT',
   url: '/api/books/:id',
   handler: booksController.updateBook
},
{
   method: 'DELETE',
   url: '/api/books/:id',
   handler: booksController.deleteBook
}
]
module.exports = routes

在上面的程式碼中,我們定義了上面提到的所有路由,並且在每個路由中,我都有一個關聯的處理程式來處理該路由。

現在,在我們能夠執行路由並對其進行測試之前,唯一剩下的步驟是首先將這些路由新增到 app 物件中,我們可以在專案根目錄中存在的 index.js 檔案中執行此操作。

index.js

// to require the framework
const app = require('fastify')({
   logger: true
})

// to declare a single route
app.get('/', function (req, reply) {
   reply.send({
      Welcome: 'TutorialsPoint'
   })
})

// Register routes to handle blog posts
const bookRoutes = require('./routes/books')

bookRoutes.forEach((route, index) => {
   app.route(route)
})

// Run the server!
app.listen(3000, (err, address) => {
   if (err) {
      app.log.error(err)
      process.exit(1)
   }
   app.log.info(`The server is listening on ${address}`)
})

現在,路由定義已完成。要測試這些路由,我們首先需要使用下面顯示的命令執行應用程式:

index.js

執行上述命令後,我們就可以開啟瀏覽器並點選 url **https://:3000/api/books/1**,這將呼叫控制器/books.js 檔案中的 **getBook** 處理程式函式,並將返回“id = 1”的書籍。

如何為我們的請求新增模式驗證?

在上一節中的程式碼中,我們沒有請求驗證,這意味著我們可以將任何內容傳遞到請求中,它將被視為有效並進入我們的程式碼,但這通常不是我們想要的。

現在,假設我們想要確保傳遞到 getBook 端點的 id 僅為物件型別,而不是任何其他型別,為此,我們可以在控制器中編寫請求驗證。

請考慮下面顯示的程式碼片段:

const getBookValidation = {
   params: {
      id: { type: 'object' }
   },
   response: {
      200: {
         type: 'object',
         properties: {
         id: { type: 'integer' },
            title: { type: 'string' },
            author: { type: 'string'}
         }
      }
   }
}

在上面的驗證中,我們確保在 params 中傳遞的 id 欄位為物件型別,而不是任何其他型別。

此外,我們還需要在控制器/books.js 中的 modules.export 中新增函式 getBookValidation。

完成控制器部分後,下一步是將函式新增到路由中,以便當我們收到該路由的請求時,我們的驗證才能生效。為此,我們需要在 getBook 路由內部編寫下面顯示的以下行。

schema: booksController.getBookValidation,

books.js

const booksController = require('../controller/books');

const routes = [{
   method: 'GET',
   url: '/api/books',
   handler: booksController.getAllBooks
},
{
   method: 'GET',
   url: '/api/books/:id',
   schema: booksController.getBookValidation,
   handler: booksController.getBook
},
{
   method: 'POST',
   url: '/api/books',
   handler: booksController.addBook
},
{
   method: 'PUT',
   url: '/api/books/:id',
   handler: booksController.updateBook
},
{
   method: 'DELETE',
   url: '/api/books/:id',
   handler: booksController.deleteBook
}
]
module.exports = routes

現在,讓我們測試路由。我們需要執行 index.js 檔案,然後訪問 URL https://:3000/api/books/{1},這樣就能按預期工作。但是,如果你嘗試在 id 引數中傳遞一個物件,比如訪問 URL https://:3000/api/books/1, 那麼你將在瀏覽器中看到以下驗證錯誤。

{
   "statusCode": 400,
   "error": "Bad Request",
   "message": "params/id must be object"
}

如何定義鉤子?

在 Fastify 中,我們可以透過使用 addHook 方法來定義鉤子。

考慮以下示例,我們將定義一個鉤子,列印我們 REST API 中註冊的所有路由。

index.js 新增鉤子

app.addHook('onRoute', (routeOptions) => {
   console.log(`Routes that are registered are: ${routeOptions.url}`)
})

以上程式碼片段需要新增到 "index.js" 檔案中,然後當我們執行該檔案時,我們將獲得以下輸出

輸出

Routes that are registered are: /
Routes that are registered are: /
Routes that are registered are: /api/books
Routes that are registered are: /api/books
Routes that are registered are: /api/books/:id
Routes that are registered are: /api/books/:id
Routes that are registered are: /api/books
Routes that are registered are: /api/books/:id
Routes that are registered are: /api/books/:id

結論

在本教程中,我們學習瞭如何使用 Fastify 建立 REST API。

更新於: 2023年6月22日

2K+ 閱讀量

開啟你的 職業生涯

透過完成課程獲得認證

開始學習
廣告

© . All rights reserved.