如何使用 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://127.0.0.1:3000

现在要测试它,我们可以使用 PostMan 或 cURL,或者简单地访问浏览器,因为它是一个简单的 GET 请求。

我将使用 cURL 命令。请考虑下面显示的命令:

curl https://127.0.0.1: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://127.0.0.1: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://127.0.0.1:3000/api/books/{1},这样就能按预期工作。但是,如果你尝试在 id 参数中传递一个对象,比如访问 URL https://127.0.0.1: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+ 阅读量

开启你的 职业生涯

通过完成课程获得认证

开始学习
广告