如何使用Fastify构建REST API
Fastify是一个主要用于JavaScript后端开发的框架。它是最轻量级的后端框架之一,如果你想避免使用像Express和Hapi这样更重量级的Node框架,那么Fastify是首选。
自其诞生以来,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 服务器,我们首先需要创建一个 JavaScript 文件。假设我们想将文件命名为 “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应用对象,并在该对象中启用了日志记录。接着,我声明了一个单一路由,响应的内容是”欢迎:TutorialsPoint”。最后一个代码块展示了我们监听端口3000的情况。
为了运行以上代码,我们需要在终端中运行以下命令。
node index.js
一旦我们运行上述命令,服务器将在以下端点上运行。
http://localhost:3000
现在,为了测试它,我们可以使用PostMan或cURL,或者只需访问浏览器,因为它是一个简单的GET请求。
我将使用cURL命令。考虑下面显示的命令:
curl http://localhost: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中删除一本书
在定义路由之前,有必要定义这些路由的控制器。
创建Books控制器
为了使我们的代码模块化和清晰,让我们创建一个名为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
一旦我们运行了上面的命令,我们就可以打开浏览器并访问以下网址: http://localhost:3000/api/books/1 这将调用我们控制器中的 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字段是对象类型,而不是其他任何类型。
同时,我们还需要在controller/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文件,然后我们需要访问以下网址, http://localhost:3000/api/books/{1} 一旦我们这样做,它将按预期工作。但是如果你尝试在id参数中传递一个对象,例如,如果你访问以下网址 http://localhost:3000/api/books/1 那么你将在浏览器中得到以下验证错误。
{
"statusCode": 400,
"error": "Bad Request",
"message": "params/id must be object"
}
如何定义Hooks
我们可以通过使用addHook方法在Fastify中定义Hooks。
考虑下面的示例,我们将定义一个Hook,该Hook将打印出我们的REST API中注册的所有路由。
index.js addHook
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。