插件概念 #
一、插件系统概述 #
Fastify的插件系统是其核心特性之一,遵循"一切皆插件"的设计理念。
1.1 什么是插件 #
插件是一个可重用的代码模块,可以:
- 注册路由
- 添加钩子
- 装饰Fastify实例
- 注册其他插件
1.2 插件的优势 #
| 优势 | 说明 |
|---|---|
| 模块化 | 将功能拆分为独立模块 |
| 可复用 | 插件可在多个项目中使用 |
| 可测试 | 独立测试每个插件 |
| 可维护 | 易于更新和维护 |
| 可扩展 | 轻松添加新功能 |
1.3 插件基本结构 #
javascript
async function myPlugin(fastify, options) {
fastify.decorate('utility', () => {
return 'Hello from plugin'
})
fastify.get('/plugin-route', async (request, reply) => {
return { message: 'From plugin' }
})
}
module.exports = myPlugin
二、插件注册 #
2.1 基本注册 #
javascript
const fastify = require('fastify')()
fastify.register(require('./my-plugin'))
fastify.listen({ port: 3000 })
2.2 带选项注册 #
javascript
fastify.register(require('./my-plugin'), {
prefix: '/api',
option1: 'value1',
option2: 'value2'
})
2.3 异步注册 #
javascript
fastify.register(async function (fastify, opts) {
await fastify.register(require('./plugin-a'))
await fastify.register(require('./plugin-b'))
})
2.4 条件注册 #
javascript
if (process.env.ENABLE_AUTH === 'true') {
fastify.register(require('./auth-plugin'))
}
三、插件作用域 #
3.1 封装作用域 #
Fastify插件默认是封装的,内部注册的内容不会泄漏到外部。
javascript
const fastify = require('fastify')()
fastify.register(async function (fastify, opts) {
fastify.decorate('internal', 'value')
fastify.get('/internal', async (request, reply) => {
return { internal: fastify.internal }
})
})
fastify.get('/external', async (request, reply) => {
return { internal: fastify.internal }
})
fastify.listen({ port: 3000 })
访问 /external 会报错,因为 internal 装饰器只在插件作用域内有效。
3.2 破坏封装 #
使用 fastify-plugin 可以破坏封装:
javascript
const fp = require('fastify-plugin')
async function myPlugin(fastify, opts) {
fastify.decorate('shared', 'global value')
}
module.exports = fp(myPlugin)
现在 shared 装饰器在整个应用中都可用。
3.3 作用域继承 #
子插件可以访问父插件的内容:
javascript
fastify.register(async function (fastify, opts) {
fastify.decorate('parent', 'value')
fastify.register(async function (fastify, opts) {
console.log(fastify.parent)
})
})
四、fastify-plugin #
4.1 基本用法 #
javascript
const fp = require('fastify-plugin')
async function myPlugin(fastify, opts) {
fastify.decorate('utility', function () {
return 'Utility function'
})
}
module.exports = fp(myPlugin)
4.2 插件元数据 #
javascript
module.exports = fp(myPlugin, {
name: 'my-plugin',
fastify: '4.x',
dependencies: ['other-plugin']
})
4.3 依赖声明 #
javascript
module.exports = fp(myPlugin, {
name: 'auth-plugin',
dependencies: ['database-plugin', 'cache-plugin']
})
4.4 封装级别控制 #
javascript
module.exports = fp(myPlugin, {
name: 'my-plugin',
encapsulate: true
})
五、装饰器 #
5.1 装饰Fastify实例 #
javascript
fastify.decorate('db', {
users: [],
addUser(user) {
this.users.push(user)
},
getUsers() {
return this.users
}
})
fastify.get('/users', async (request, reply) => {
return fastify.db.getUsers()
})
5.2 装饰Request #
javascript
fastify.decorateRequest('user', null)
fastify.addHook('onRequest', async (request, reply) => {
request.user = { id: 1, name: 'John' }
})
fastify.get('/me', async (request, reply) => {
return request.user
})
5.3 装饰Reply #
javascript
fastify.decorateReply('success', function (data) {
return this.send({
success: true,
data
})
})
fastify.get('/data', async (request, reply) => {
reply.success({ message: 'Hello' })
})
5.4 检查装饰器是否存在 #
javascript
if (!fastify.hasDecorator('db')) {
fastify.decorate('db', createDatabase())
}
if (!fastify.hasRequestDecorator('user')) {
fastify.decorateRequest('user', null)
}
5.5 装饰器最佳实践 #
javascript
const fp = require('fastify-plugin')
async function databasePlugin(fastify, opts) {
if (fastify.hasDecorator('db')) {
throw new Error('Database decorator already exists')
}
const db = await createConnection(opts)
fastify.decorate('db', db)
fastify.addHook('onClose', async (instance) => {
await instance.db.close()
})
}
module.exports = fp(databasePlugin, {
name: 'database'
})
六、插件生命周期 #
6.1 注册顺序 #
javascript
fastify.register(async function (fastify, opts) {
console.log('Plugin A')
})
fastify.register(async function (fastify, opts) {
console.log('Plugin B')
})
fastify.ready(() => {
console.log('All plugins loaded')
})
6.2 异步初始化 #
javascript
fastify.register(async function (fastify, opts) {
const connection = await connectDatabase()
fastify.decorate('db', connection)
console.log('Database connected')
})
fastify.ready(err => {
if (err) throw err
console.log('App ready, database is available')
})
6.3 插件就绪检测 #
javascript
fastify.after((err) => {
if (err) throw err
console.log('Previous plugins are ready')
})
fastify.ready((err) => {
if (err) throw err
console.log('All plugins are ready')
})
七、插件配置 #
7.1 环境配置 #
javascript
const fp = require('fastify-plugin')
async function configPlugin(fastify, opts) {
const config = {
development: {
db: 'mongodb://localhost:27017/dev'
},
production: {
db: process.env.DB_URL
}
}
const env = process.env.NODE_ENV || 'development'
fastify.decorate('config', config[env])
}
module.exports = fp(configPlugin)
7.2 选项验证 #
javascript
async function myPlugin(fastify, opts) {
if (!opts.apiKey) {
throw new Error('apiKey is required')
}
if (typeof opts.timeout !== 'number') {
opts.timeout = 5000
}
fastify.decorate('api', {
key: opts.apiKey,
timeout: opts.timeout
})
}
7.3 默认选项 #
javascript
const fp = require('fastify-plugin')
async function myPlugin(fastify, opts) {
const options = {
enabled: true,
timeout: 5000,
retries: 3,
...opts
}
fastify.decorate('options', options)
}
module.exports = fp(myPlugin)
八、插件类型 #
8.1 功能插件 #
提供特定功能的插件:
javascript
const fp = require('fastify-plugin')
async function emailPlugin(fastify, opts) {
const nodemailer = require('nodemailer')
const transporter = nodemailer.createTransport(opts)
fastify.decorate('email', {
async send(to, subject, body) {
await transporter.sendMail({
from: opts.from,
to,
subject,
html: body
})
}
})
}
module.exports = fp(emailPlugin, {
name: 'email'
})
8.2 路由插件 #
提供路由的插件:
javascript
module.exports = async function (fastify, opts) {
fastify.get('/health', async (request, reply) => {
return { status: 'ok' }
})
fastify.get('/ready', async (request, reply) => {
return { ready: true }
})
}
8.3 集成插件 #
集成第三方服务的插件:
javascript
const fp = require('fastify-plugin')
async function redisPlugin(fastify, opts) {
const Redis = require('ioredis')
const redis = new Redis(opts.url)
fastify.decorate('redis', redis)
fastify.addHook('onClose', async (instance) => {
await instance.redis.quit()
})
}
module.exports = fp(redisPlugin, {
name: 'redis'
})
8.4 中间件插件 #
提供中间件功能的插件:
javascript
const fp = require('fastify-plugin')
async function authPlugin(fastify, opts) {
fastify.decorate('authenticate', async function (request, reply) {
const token = request.headers.authorization
if (!token) {
reply.code(401).send({ error: 'Unauthorized' })
return
}
try {
request.user = await verifyToken(token)
} catch (err) {
reply.code(401).send({ error: 'Invalid token' })
}
})
}
module.exports = fp(authPlugin, {
name: 'auth'
})
九、插件调试 #
9.1 打印插件树 #
javascript
fastify.ready(() => {
console.log(fastify.printPlugins())
})
9.2 检查装饰器 #
javascript
fastify.ready(() => {
console.log('Has db:', fastify.hasDecorator('db'))
console.log('Has user:', fastify.hasRequestDecorator('user'))
})
9.3 插件加载错误 #
javascript
fastify.register(require('./my-plugin'))
fastify.ready(err => {
if (err) {
console.error('Plugin loading failed:', err)
process.exit(1)
}
})
十、最佳实践 #
10.1 插件命名 #
javascript
module.exports = fp(myPlugin, {
name: '@myorg/fastify-mysql'
})
10.2 错误处理 #
javascript
async function myPlugin(fastify, opts) {
try {
const connection = await connect(opts)
fastify.decorate('db', connection)
} catch (err) {
fastify.log.error('Failed to connect:', err)
throw err
}
}
10.3 资源清理 #
javascript
async function myPlugin(fastify, opts) {
const resource = await createResource()
fastify.decorate('resource', resource)
fastify.addHook('onClose', async (instance) => {
await instance.resource.close()
})
}
10.4 文档化 #
javascript
/**
* Fastify MySQL Plugin
*
* @param {FastifyInstance} fastify - Fastify instance
* @param {Object} opts - Plugin options
* @param {string} opts.host - MySQL host
* @param {number} opts.port - MySQL port
* @param {string} opts.database - Database name
* @param {string} opts.user - Database user
* @param {string} opts.password - Database password
*/
async function mysqlPlugin(fastify, opts) {
// Implementation
}
十一、总结 #
本章我们学习了:
- 插件概念:什么是插件,插件的优势
- 插件注册:基本注册、带选项注册、异步注册
- 插件作用域:封装作用域、破坏封装、作用域继承
- fastify-plugin:基本用法、元数据、依赖声明
- 装饰器:装饰实例、Request、Reply
- 插件生命周期:注册顺序、异步初始化、就绪检测
- 插件类型:功能插件、路由插件、集成插件、中间件插件
- 插件调试:打印插件树、检查装饰器
- 最佳实践:命名、错误处理、资源清理
接下来让我们学习如何注册插件!
最后更新:2026-03-28