入门
入门
¥Getting Started
你好!感谢你查看 Fastify!
¥Hello! Thank you for checking out Fastify!
本文档旨在简要介绍该框架及其功能。这是一个基本的前言,包含示例和文档其他部分的链接。
¥This document aims to be a gentle introduction to the framework and its features. It is an elementary preface with examples and links to other parts of the documentation.
开始吧!
¥Let's start!
安装
¥Install
使用 npm 安装:
¥Install with npm:
npm i fastify
用 Yarn 安装:
¥Install with yarn:
yarn add fastify
你的第一台服务器
¥Your first server
让我们编写我们的第一个服务器:
¥Let's write our first server:
// 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),请务必包含 "type":package.json 中的 "module"。
¥If you are using ECMAScript Modules (ESM) in your project, be sure to include "type": "module" in your package.json.
{
"type": "module"
}
你更喜欢使用 async/await
吗?Fastify 开箱即用地支持它。
¥Do you prefer to use async/await
? Fastify supports it out-of-the-box.
// 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()
太棒了,这很容易。
¥Awesome, that was easy.
不幸的是,编写复杂的应用需要比这个示例更多的代码。构建新应用时的一个经典问题是如何处理多个文件、异步引导和代码架构。
¥Unfortunately, writing a complex application requires significantly more code than this example. A classic problem when you are building a new application is how to handle multiple files, asynchronous bootstrapping, and the architecture of your code.
Fastify 提供了一个简单的平台,有助于解决上述所有问题以及更多问题!
¥Fastify offers an easy platform that helps to solve all of the problems outlined above, and more!
注意 上述示例以及本文档中的后续示例默认仅在 localhost
127.0.0.1
接口上进行监听。要监听所有可用的 IPv4 接口,应修改示例以监听0.0.0.0
,如下所示:¥The above examples, and subsequent examples in this document, default to listening only on the localhost
127.0.0.1
interface. To listen on all available IPv4 interfaces the example should be modified to listen on0.0.0.0
like so: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 地址上的连接。¥Similarly, specify
::1
to accept only local connections via IPv6. Or specify::
to accept connections on all IPv6 addresses, and, if the operating system supports it, also on all IPv4 addresses.当部署到 Docker(或其他类型)容器时,使用
0.0.0.0
或::
将是公开应用的最简单方法。¥When deploying to a Docker (or another type of) container using
0.0.0.0
or::
would be the easiest method for exposing the application.请注意,使用
0.0.0.0
时,上面回调参数中提供的地址将是通配符引用的第一个地址。¥Note that when using
0.0.0.0
, the address provided in the callback argument above will be the first address the wildcard refers to.
你的第一个插件
¥Your first plugin
与 JavaScript 一样,一切都是对象,而在 Fastify 中,一切都是插件。
¥As with JavaScript, where everything is an object, with Fastify everything is a plugin.
在深入研究之前,让我们看看它是如何工作的!
¥Before digging into it, let's see how it works!
让我们声明我们的基本服务器,但不是在入口点内声明路由,而是在外部文件中声明它(查看 路由声明 文档)。
¥Let's declare our basic server, but instead of declaring the route inside the entry point, we'll declare it in an external file (check out the route declaration docs).
// 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.dev/docs/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 框架的核心。这是添加路由、插件等的唯一方法。
¥In this example, we used the register
API, which is the core of the Fastify
framework. It is the only way to add routes, plugins, et cetera.
在本指南的开头,我们注意到 Fastify 提供了一个有助于异步引导应用的基础。为什么这很重要?
¥At the beginning of this guide, we noted that Fastify provides a foundation that assists with asynchronous bootstrapping of your application. Why is this important?
考虑需要数据库连接来处理数据存储的场景。在服务器接受连接之前,数据库连接需要可用。我们如何解决这个问题?
¥Consider the scenario where a database connection is needed to handle data storage. The database connection needs to be available before the server is accepting connections. How do we address this problem?
典型的解决方案是使用复杂的回调或 promise - 将框架 API 与其他库和应用代码混合在一起的系统。
¥A typical solution is to use a complex callback, or promises - a system that will mix the framework API with other libraries and the application code.
Fastify 在内部以最小的努力处理这个问题!
¥Fastify handles this internally, with minimum effort!
让我们用数据库连接重写上面的例子。
¥Let's rewrite the above example with a database connection.
首先,安装 fastify-plugin
和 @fastify/mongodb
:
¥First, install fastify-plugin
and @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://localhost: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.dev/docs/Reference/Plugins/#plugin-options
*/
async function dbConnector (fastify, options) {
fastify.register(require('@fastify/mongodb'), {
url: 'mongodb://localhost: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.dev/docs/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
哇,太快了!
¥Wow, that was fast!
让我们回顾一下自从引入一些新概念以来我们在这里所做的事情。
¥Let's recap what we have done here since we've introduced some new concepts.
如你所见,我们将 register
用于数据库连接器和路由注册。
¥As you can see, we used register
for both the database connector and the
registration of the routes.
这是 Fastify 最好的功能之一,它会按照你声明插件的顺序加载你的插件,并且仅在当前插件加载后才会加载下一个插件。这样,我们可以在第一个插件中注册数据库连接器,并在第二个插件中使用它(阅读 此处 以了解如何处理插件的范围)。
¥This is one of the best features of Fastify, it will load your plugins in the same order you declare them, and it will load the next plugin only once the current one has been loaded. In this way, we can register the database connector in the first plugin and use it in the second (read here to understand how to handle the scope of a plugin).
当你调用 fastify.listen()
、fastify.inject()
或 fastify.ready()
时,插件加载开始
¥Plugin loading starts when you call fastify.listen()
, fastify.inject()
or
fastify.ready()
MongoDB 插件使用 decorate
API 将自定义对象添加到 Fastify 实例,使它们可在任何地方使用。鼓励使用此 API 以方便代码重用并减少代码或逻辑重复。
¥The MongoDB plugin uses the decorate
API to add custom objects to the Fastify
instance, making them available for use everywhere. Use of this API is
encouraged to facilitate easy code reuse and to decrease code or logic
duplication.
要深入了解 Fastify 插件的工作原理、如何开发新插件,以及如何使用整个 Fastify API 来处理异步引导应用的复杂性的详细信息,请阅读 插件指南。
¥To dig deeper into how Fastify plugins work, how to develop new plugins, and for details on how to use the whole Fastify API to deal with the complexity of asynchronously bootstrapping an application, read the hitchhiker's guide to plugins.
插件的加载顺序
¥Loading order of your plugins
为了保证应用的行为一致且可预测,我们强烈建议始终加载你的代码,如下所示:
¥To guarantee consistent and predictable behavior of your application, we highly recommend to always load your code as shown below:
└── plugins (from the Fastify ecosystem)
└── your plugins (your custom plugins)
└── decorators
└── hooks
└── your services
这样,你将始终可以访问当前作用域中声明的所有属性。
¥In this way, you will always have access to all of the properties declared in the current scope.
如前所述,Fastify 提供了一个可靠的封装模型,可帮助你将应用构建为独立服务。如果你只想为一部分路由注册插件,则只需复制上述结构即可。
¥As discussed previously, Fastify offers a solid encapsulation model, to help you build your application as independent services. If you want to register a plugin only for a subset of routes, you just have to replicate the above structure.
└── 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
验证你的数据
¥Validate your data
数据验证非常重要,也是该框架的核心概念。
¥Data validation is extremely important and a core concept of the framework.
要验证传入的请求,Fastify 使用 JSON 结构。
¥To validate incoming requests, Fastify uses JSON Schema.
让我们看一个演示路由验证的示例:
¥Let's look at an example demonstrating validation for routes:
/**
* @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' }
})
此示例显示如何将选项对象传递给路由,该路由接受包含路由、body
、querystring
、params
和 headers
的所有架构的 schema
键。
¥This example shows how to pass an options object to the route, which accepts a
schema
key that contains all of the schemas for route, body
, querystring
,
params
, and headers
.
阅读 验证和序列化 以了解更多信息。
¥Read Validation and Serialization to learn more.
序列化你的数据
¥Serialize your data
Fastify 对 JSON 具有一流的支持。它针对解析 JSON 主体和序列化 JSON 输出进行了极其优化。
¥Fastify has first-class support for JSON. It is extremely optimized to parse JSON bodies and serialize JSON output.
要加速 JSON 序列化(是的,它很慢!)请使用 schema 选项的 response
键,如以下示例所示:
¥To speed up JSON serialization (yes, it is slow!) use the response
key of the
schema option as shown in the following example:
/**
* @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 将仅序列化响应模式中存在的数据。阅读 验证和序列化 以了解更多信息。
¥By specifying a schema as shown, you can speed up serialization by a factor of 2-3. This also helps to protect against leakage of potentially sensitive data, since Fastify will serialize only the data present in the response schema. Read Validation and Serialization to learn more.
解析请求负载
¥Parsing request payloads
Fastify 原生解析 'application/json'
和 'text/plain'
请求负载,结果可从 request.body
的 Fastify 请求 对象访问。
¥Fastify parses 'application/json'
and 'text/plain'
request payloads
natively, with the result accessible from the Fastify
request object at request.body
.
以下示例将解析后的请求正文返回给客户端:
¥The following example returns the parsed body of a request back to the client:
/**
* @type {import('fastify').RouteShorthandOptions}
*/
const opts = {}
fastify.post('/', opts, async (request, reply) => {
return request.body
})
阅读 内容类型解析器 以了解有关 Fastify 的默认解析功能以及如何支持其他内容类型的更多信息。
¥Read Content-Type Parser to learn more about Fastify's default parsing functionality and how to support other content types.
扩展你的服务器
¥Extend your server
Fastify 的构建具有极高的可扩展性和极简性,我们相信一个简单的框架就足以使出色的应用成为可能。
¥Fastify is built to be extremely extensible and minimal, we believe that a bare-bones framework is all that is necessary to make great applications possible.
换句话说,Fastify 不是 "适配器已包含" 框架,而是依赖于令人惊叹的 ecosystem!
¥In other words, Fastify is not a "batteries included" framework, and relies on an amazing ecosystem!
测试你的服务器
¥Test your server
Fastify 不提供测试框架,但我们确实推荐一种使用 Fastify 的功能和架构来编写测试的方法。
¥Fastify does not offer a testing framework, but we do recommend a way to write your tests that uses the features and architecture of Fastify.
阅读 testing 文档以了解更多信息!
¥Read the testing documentation to learn more!
从 CLI 运行你的服务器
¥Run your server from CLI
Fastify 还通过 fastify-cli 进行 CLI 集成,fastify-cli 是一个用于搭建和管理 Fastify 项目的单独工具。
¥Fastify also has CLI integration via fastify-cli, a separate tool for scaffolding and managing Fastify projects.
首先,安装 fastify-cli
:
¥First, install fastify-cli
:
npm i fastify-cli
你还可以使用 -g
全局安装它。
¥You can also install it globally with -g
.
然后,将以下行添加到 package.json
:
¥Then, add the following lines to package.json
:
{
"scripts": {
"start": "fastify start server.js"
}
}
并创建你的服务器文件:
¥And create your server file(s):
// server.js
'use strict'
module.exports = async function (fastify, opts) {
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
}
然后运行你的服务器:
¥Then run your server with:
npm start
幻灯片和视频
¥Slides and Videos
-
幻灯片
¥Slides
-
视频
¥Videos