跳至主要内容
版本: 最新版本 (v5.0.x)

快速入门

快速入门

您好!感谢您选择 Fastify!

本文档旨在为您提供框架及其功能的入门介绍。它是一个包含示例和指向文档其他部分链接的基本前言。

让我们开始吧!

安装

使用 npm 安装

npm i fastify

使用 yarn 安装

yarn add fastify

您的第一个服务器

让我们编写第一个服务器

// Require the framework and instantiate it

// ESM
import Fastify from 'fastify'

const fastify = Fastify({
logger: true
})
// CommonJs
const fastify = require('fastify')({
logger: true
})

// Declare a route
fastify.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})

// Run the server!
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${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' }
})

/**
* Run the server!
*/
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 接口,应将示例修改为监听 0.0.0.0,如下所示

fastify.listen({ port: 3000, host: '0.0.0.0' }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
fastify.log.info(`server listening on ${address}`)
})

类似地,指定 ::1 以仅通过 IPv6 接受本地连接。或者指定 :: 以接受所有 IPv6 地址上的连接,以及(如果操作系统支持)所有 IPv4 地址上的连接。

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

您的第一个插件

与 JavaScript 中一切皆为对象一样,在 Fastify 中一切皆为插件。

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

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

// ESM
import Fastify from 'fastify'
import firstRoute from './our-first-route.js'
/**
* @type {import('fastify').FastifyInstance} Instance of 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)
}
// Server is now listening on ${address}
})
// CommonJs
/**
* @type {import('fastify').FastifyInstance} Instance of 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)
}
// Server is now listening on ${address}
})
// our-first-route.js

/**
* Encapsulates the routes
* @param {FastifyInstance} fastify Encapsulated Fastify Instance
* @param {Object} options plugin options, refer to https://fastify.com.cn/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} Instance of 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)
}
// Server is now listening on ${address}
})
// CommonJs
/**
* @type {import('fastify').FastifyInstance} Instance of Fastify
*/
const fastify = require('fastify')({
logger: true
})

fastify.register(require('./our-db-connector'))
fastify.register(require('./our-first-route'))

fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${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://127.0.0.1:27017/test_database'
})
}

// Wrapping a plugin function with fastify-plugin exposes the decorators
// and hooks, declared inside the plugin to the parent scope.
export default fastifyPlugin(dbConnector)

// CommonJs
/**
* @type {import('fastify-plugin').FastifyPlugin}
*/
const fastifyPlugin = require('fastify-plugin')


/**
* Connects to a MongoDB database
* @param {FastifyInstance} fastify Encapsulated Fastify Instance
* @param {Object} options plugin options, refer to https://fastify.com.cn/docs/latest/Reference/Plugins/#plugin-options
*/
async function dbConnector (fastify, options) {
fastify.register(require('@fastify/mongodb'), {
url: 'mongodb://127.0.0.1:27017/test_database'
})
}

// Wrapping a plugin function with fastify-plugin exposes the decorators
// and hooks, declared inside the plugin to the parent scope.
module.exports = fastifyPlugin(dbConnector)

our-first-route.js

/**
* A plugin that provide encapsulated routes
* @param {FastifyInstance} fastify encapsulated fastify instance
* @param {Object} options plugin options, refer to https://fastify.com.cn/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('No documents found')
}
return result
})

fastify.get('/animals/:animal', async (request, reply) => {
const result = await collection.findOne({ animal: request.params.animal })
if (!result) {
throw new Error('Invalid value')
}
return result
})

const animalBodyJsonSchema = {
type: 'object',
required: ['animal'],
properties: {
animal: { type: 'string' },
},
}

const schema = {
body: animalBodyJsonSchema,
}

fastify.post('/animals', { schema }, async (request, reply) => {
// we can use the `request.body` object to get the data sent by the client
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 (from the Fastify ecosystem)
└── your plugins (your custom plugins)
└── decorators
└── hooks
└── your services

这样,您将始终可以访问当前作用域中声明的所有属性。

如前所述,Fastify 提供了一个可靠的封装模型,以帮助您将应用程序构建为单个且独立的服务。如果您只想为部分路由注册插件,只需复制上述结构即可。

└── plugins (from the Fastify ecosystem)
└── your plugins (your custom plugins)
└── decorators
└── hooks
└── your services

└── service A
│ └── plugins (from the Fastify ecosystem)
│ └── your plugins (your custom plugins)
│ └── decorators
│ └── hooks
│ └── your services

└── service B
└── plugins (from the Fastify ecosystem)
└── your plugins (your custom 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' }
})

此示例演示了如何将选项对象传递给路由,该选项对象接受一个 schema 键,其中包含路由、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 的默认解析功能以及如何支持其他内容类型的更多信息。

扩展您的服务器

Fastify 构建为极具扩展性和最小化,我们认为一个精简的框架是构建出色应用程序所必需的。

换句话说,Fastify 不是一个“包含电池”的框架,而是依赖于一个令人惊叹的 生态系统

测试您的服务器

Fastify 没有提供测试框架,但我们确实推荐了一种使用 Fastify 的功能和架构编写测试的方法。

阅读 测试 文档以了解更多信息!

从 CLI 运行您的服务器

由于 fastify-cli,Fastify 还具有 CLI 集成。

首先,安装 fastify-cli

npm i fastify-cli

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

然后,将以下行添加到 package.json

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

并创建您的服务器文件

// server.js
'use strict'

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

然后使用以下命令运行您的服务器

npm start

幻灯片和视频