Skip to Content

快速入门

您好!感谢您查看 Fastify!

本文档旨在为您提供一个框架及其功能的简单介绍。它包含示例和链接,指向文档中的其他部分。

让我们开始吧!

安装

使用 npm 安装:

npm i fastify

使用 yarn 安装:

yarn add fastify

创建你的第一个服务器

让我们编写第一个服务器:

// 引入框架并实例化 // ESM import Fastify from 'fastify' const fastify = Fastify({ logger: true }) // CommonJs const fastify = require('fastify')({ logger: true }) // 声明一个路由 fastify.get('/', function (request, reply) { reply.send({ hello: 'world' }) }) // 启动服务器! fastify.listen({ port: 3000 }, function (err, address) { if (err) { fastify.log.error(err) process.exit(1) } // 服务器现在正在 ${address} 监听 })

如果你在项目中使用 ECMAScript 模块(ESM),请确保在你的 package.json 中包含 "type": "module"

{ "type": "module" }

你更喜欢使用 async/await 吗?Fastify 默认支持它。

// ESM import Fastify from 'fastify' const fastify = Fastify({ logger: true }) // CommonJs const fastify = require('fastify')({ logger: true }) fastify.get('/', async (request, reply) => { return { hello: 'world' } }) /** * 启动服务器! */ const start = async () => { try { await fastify.listen({ port: 3000 }) } catch (err) { fastify.log.error(err) process.exit(1) } } start()

太好了,这很简单。

不幸的是,编写一个复杂的应用程序需要比这个示例多得多的代码。 当你构建一个新的应用程序时,经典的问题是如何处理多个文件、异步启动和代码架构。

Fastify 提供了一个易于使用的平台,可以帮助解决上述所有问题,并且还有更多!

注意 上面的例子以及本文档后续的例子,默认情况下只在本地主机 127.0.0.1 接口上监听。要在一个或多个可用的 IPv4 接口上监听,请将示例修改为如下所示:

fastify.listen({ port: 3000, host: '0.0.0.0' }, function (err, address) { if (err) { fastify.log.error(err) process.exit(1) } fastify.log.info(`服务器在 ${address} 上监听`) })

类似地,指定 ::1 以仅接受本地 IPv6 连接。或者指定 :: 以接受所有 IPv6 地址上的连接,并且如果操作系统支持的话,也包括所有 IPv4 地址。

当部署到 Docker(或其他类型的)容器时,使用 0.0.0.0:: 是暴露应用程序的最简单方法。

注意,在使用 0.0.0.0 时,回调参数中的地址将是通配符所指的第一个地址。

你的第一个插件

就像 JavaScript 中的万物皆对象一样,在 Fastify 中,一切都是一个插件。

在深入探讨之前,让我们看看它是如何工作的!

我们声明我们的基本服务器,但不是在入口点中声明路由,而是在外部文件中声明它(请参阅 路由声明 文档)。

// ESM import Fastify from 'fastify' import firstRoute from './our-first-route.js' /** * @type {import('fastify').FastifyInstance} Fastify 实例 */ const fastify = Fastify({ logger: true }) fastify.register(firstRoute) fastify.listen({ port: 3000 }, function (err, address) { if (err) { fastify.log.error(err) process.exit(1) } // 服务器现在正在 ${address} 监听 })
// CommonJs /** * @type {import('fastify').FastifyInstance} Fastify 实例 */ const fastify = require('fastify')({ logger: true }) fastify.register(require('./our-first-route')) fastify.listen({ port: 3000 }, function (err, address) { if (err) { fastify.log.error(err) process.exit(1) } // 服务器现在正在 ${address} 监听 })
// our-first-route.js /** * 封装路由 * @param {FastifyInstance} fastify 封装的 Fastify 实例 * @param {Object} options 插件选项,参见 https://fastify.dev/docs/latest/Reference/Plugins/#plugin-options */ async function routes (fastify, options) { fastify.get('/', async (request, reply) => { return { hello: 'world' } }) } //ESM export default routes; // CommonJs module.exports = routes

在本示例中,我们使用了 register API,这是 Fastify 框架的核心。它是添加路由、插件等的唯一方式。

在本指南开始时,我们提到 Fastify 提供了一个基础框架,有助于异步启动你的应用程序。为什么这很重要?

考虑一个场景,在该场景下需要数据库连接来处理数据存储。数据库连接必须在服务器接受连接之前可用。如何解决这个问题?

一种典型的解决方案是使用复杂的回调或 Promise —— 这种系统将混合框架 API 和其他库以及应用代码。

Fastify 内部处理了这一点,只需最少的努力!

让我们用一个数据库连接重写上面的示例。

首先安装 fastify-plugin@fastify/mongodb

npm i fastify-plugin @fastify/mongodb

server.js

// ESM import Fastify from 'fastify' import dbConnector from './our-db-connector.js' import firstRoute from './our-first-route.js' /** * @type {import('fastify').FastifyInstance} Fastify 实例 */ const fastify = Fastify({ logger: true }) fastify.register(dbConnector) fastify.register(firstRoute) fastify.listen({ port: 3000 }, function (err, address) { if (err) { fastify.log.error(err) process.exit(1) } // 服务器现在监听 ${address} })
// CommonJs /** * @type {import('fastify').FastifyInstance} Fastify 实例 */ const fastify = require('fastify')({ logger: true }) fastify.register(require('./our-db-connector')) fastify.register(require('./我们的第一个路由')) fastify.listen({ port: 3000 }, function (err, address) { if (err) { fastify.log.error(err) process.exit(1) } // 服务器现在监听 ${address} })

our-db-connector.js

// ESM import fastifyPlugin from 'fastify-plugin' import fastifyMongo from '@fastify/mongodb' /** * @param {FastifyInstance} fastify * @param {Object} options */ async function dbConnector (fastify, options) { fastify.register(fastifyMongo, { url: 'mongodb://localhost:27017/test_database' }) } // 使用 fastify-plugin 包装插件函数,将插件内部声明的装饰器和钩子暴露给父作用域。 export default fastifyPlugin(dbConnector)
// CommonJs /** * @type {import('fastify-plugin').FastifyPlugin} */ const fastifyPlugin = require('fastify-plugin') /** * 连接到 MongoDB 数据库 * @param {FastifyInstance} fastify 封装的 Fastify 实例 * @param {Object} options 插件选项,参见 https://fastify.dev/docs/latest/Reference/Plugins/#plugin-options */ async function dbConnector (fastify, options) { fastify.register(require('@fastify/mongodb'), { url: 'mongodb://localhost:27017/test_database' }) } // 使用 fastify-plugin 包装插件函数,将插件内部声明的装饰器和钩子暴露给父作用域。 module.exports = fastifyPlugin(dbConnector)

our-first-route.js.js

/** * 提供封装的路由插件 * @param {FastifyInstance} fastify 封装后的 fastify 实例 * @param {Object} options 插件选项,参见 https://fastify.dev/docs/latest/Reference/Plugins/#plugin-options */ async function routes (fastify, options) { const collection = fastify.mongo.db.collection('test_collection') fastify.get('/', async (request, reply) => { return { hello: 'world' } }) fastify.get('/animals', async (request, reply) => { const result = await collection.find().toArray() if (result.length === 0) { throw new Error('没有找到文档') } return result }) fastify.get('/animals/:animal', async (request, reply) => { const result = await collection.findOne({ animal: request.params.animal }) if (!result) { throw new Error('无效值') } return result }) const animalBodyJsonSchema = { type: 'object', required: ['animal'], properties: { animal: { type: 'string' }, }, } const schema = { body: animalBodyJsonSchema, } fastify.post('/animals', { schema }, async (request, reply) => { // 我们可以使用 `request.body` 对象来获取客户端发送的数据 const result = await collection.insertOne({ animal: request.body.animal }) return result }) } module.exports = routes

哇,好快!

让我们回顾一下自引入一些新概念以来我们所做的一切。

如你所见,我们在数据库连接器和路由注册中都使用了 register

这是 Fastify 最棒的功能之一。它会按照你在代码中声明的顺序加载插件,并且只有当前插件加载完成后才会继续加载下一个插件。这样,我们就可以在第一个插件中注册数据库连接器并在第二个插件中使用它(了解如何处理插件作用域,请参阅此处)。

插件加载从调用 fastify.listen()fastify.inject()fastify.ready() 开始。

MongoDB 插件使用 decorate API 向 Fastify 实例添加自定义对象,使其在任何地方都可以使用。推荐使用此 API 以方便代码重用并减少代码或逻辑重复。

要深入了解 Fastify 插件的工作原理、如何开发新插件以及如何使用整个 Fastify API 来处理异步启动应用程序的复杂性,请阅读插件指南

插件加载顺序

为了保证应用程序的一致性和可预测行为,我们强烈建议您始终按照以下方式加载代码:

└── plugins(来自 Fastify 生态系统) └── your plugins(您的自定义插件) └── decorators └── hooks └── your services

通过这种方式,您可以访问当前作用域中声明的所有属性。

如前所述,Fastify 提供了一个强大的封装模型,帮助您构建独立的服务。如果您只想为一部分路由注册一个插件,则只需复制上述结构。

└── plugins(来自 Fastify 生态系统) └── your plugins(您的自定义插件) └── decorators └── hooks └── your services └── 服务 A │ └── plugins(来自 Fastify 生态系统) │ └── your plugins(您的自定义插件) │ └── decorators │ └── hooks │ └── your services └── 服务 B └── plugins(来自 Fastify 生态系统) └── your plugins(您的自定义插件) └── decorators └── hooks └── your services

验证您的数据

数据验证非常重要,是框架的核心概念之一。

为了验证传入的请求,Fastify 使用 JSON Schema 

让我们来看一个示例,展示如何为路由进行验证:

/** * @type {import('fastify').RouteShorthandOptions} * @const */ const opts = { schema: { body: { type: 'object', properties: { someKey: { type: 'string' }, someOtherKey: { type: 'number' } } } } } fastify.post('/', opts, async (request, reply) => { return { hello: 'world' } })

此示例展示了如何将包含路由、bodyquerystringparamsheaders 的所有模式的选项对象传递给路由。

了解更多请阅读 验证和序列化

序列化您的数据

Fastify 对 JSON 提供了全面支持。它经过高度优化,可以解析 JSON 身体并序列化 JSON 输出。

为了加快 JSON 序列化的速度(是的,这很慢!),请使用模式选项中的 response 键,如以下示例所示:

/** * @type {import('fastify').RouteShorthandOptions} * @const */ const opts = { schema: { response: { 200: { type: 'object', properties: { hello: { type: 'string' } } } } } } fastify.get('/', opts, async (request, reply) => { return { hello: 'world' } })

通过指定如上所示的模式,您可以将序列化速度提高 2-3 倍。这还有助于防止潜在敏感数据的泄露,因为 Fastify 只会根据响应模式中的数据进行序列化。了解更多请阅读 验证和序列化

请求负载解析

Fastify 原生支持 'application/json''text/plain' 类型的请求负载解析,解析结果可以通过 Fastify 请求对象 中的 request.body 访问。

以下示例将返回请求体的内容给客户端:

/** * @type {import('fastify').RouteShorthandOptions} */ const opts = {} fastify.post('/', opts, async (request, reply) => { return request.body })

了解更多关于 Fastify 默认解析功能以及如何支持其他内容类型的信息,请参阅 Content-Type 解析器

扩展你的服务器

Fastify 被设计为极其可扩展且精简,我们认为一个基础框架就足以构建出优秀的应用程序了。

换句话说,Fastify 不是一个“开箱即用”的框架,并依赖于一个出色的 生态系统

测试你的服务器

Fastify 没有提供测试框架,但我们推荐一种使用 Fastify 特性和架构来编写测试的方法。

了解更多,请参阅 测试文档

从命令行运行你的服务器

Fastify 还通过 fastify-cli ,一个用于搭建和管理 Fastify 项目的独立工具,提供了 CLI 集成。

首先安装 fastify-cli

npm i fastify-cli

你也可以使用 -g 全局安装它。

然后,在 package.json 中添加以下内容:

{ "scripts": { "start": "fastify start server.js" } }

接着创建你的服务器文件(例如 server.js):

// server.js 'use strict' module.exports = async function (fastify, opts) { fastify.get('/', async (request, reply) => { return { hello: 'world' } }) }

最后,使用以下命令运行你的服务器:

npm start

演示文稿和视频