如何使用 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。
数据结构
网络
关系型数据库管理系统
操作系统
Java
iOS
HTML
CSS
Android
Python
C 编程
C++
C#
MongoDB
MySQL
Javascript
PHP