环企首页
Fastify
概述
Fastify 是一个专注于高性能的 Node.js Web 框架,号称是目前最快的 HTTP 框架之一。核心特点包括:
- 高吞吐量:基于 Node.js 原生
http模块和re2(正则引擎)优化,性能远高于 Express - 内置请求校验:通过 JSON Schema 自动验证请求参数,无需额外引入校验库
- JSON 序列化优化:内置极速 JSON 序列化器,直接输出二进制而非字符串
- 插件系统:每个插件都是独立的 Fastify 实例,支持嵌套和封装
- 开发者体验:内置日志(pino)、提供 Swagger 文档生成、热重载等工具
适合场景:
- 高并发 API 服务(QPS 敏感型)
- 对响应延迟有严格要求的实时服务
- 需要严格数据校验的 API
- 微服务架构中的高性能节点
性能对比(大致参考):
Fastify > Koa > Express
(同等硬件下 Fastify QPS 约为 Express 的 2-3 倍)
常用函数详解
创建实例
const fastify = require('fastify')({ logger: true })
// 等同于
const fastify = require('fastify')({
logger: {
level: 'info',
transport: {
target: 'pino-pretty', // 本地开发美化日志
options: { colorize: true }
}
}
})
// 关闭日志(生产环境)
const fastify = require('fastify')({ logger: false })
注册路由
fastify.get/post/put/delete/patch/head/options(path, options, handler)
// 最简写法
fastify.get('/hello', (req, reply) => {
reply.send({ hello: 'world' })
})
// 带 schema 校验
fastify.get('/hello/:name', {
schema: {
params: {
type: 'object',
properties: { name: { type: 'string', minLength: 1 } }
},
querystring: {
type: 'object',
properties: {
age: { type: 'integer', minimum: 0, maximum: 150 }
}
},
response: {
200: {
type: 'object',
properties: {
message: { type: 'string' },
name: { type: 'string' },
age: { type: 'number' }
}
}
}
}
}, (req, reply) => {
reply.send({ message: 'hello', name: req.params.name, age: req.query.age })
})
// options 可省略,handler 直接跟在路径后
fastify.post('/users', async (req, reply) => {
return { id: 1 } // async 函数直接 return 即响应
})
// head 请求(无响应体)
fastify.head('/resource', (req, reply) => {
reply.header('ETag', '"abc123"').send()
})
fastify.route(options)
完整路由定义,支持更细粒度的配置。
fastify.route({
method: ['GET', 'POST'],
url: '/users',
schema: { ... },
handler: (req, reply) => { },
onRequest: (req, reply, done) => { done() }, // 请求钩子
preHandler: (req, reply, done) => { done() }, // 路由前置处理
onResponse: (req, reply, done) => { done() }, // 响应后钩子
onTimeout: (req, reply, done) => { done() }, // 超时钩子
preSerialization: (req, reply, payload, done) => { done(null, payload) }
})
fastify.all(path, handler)
处理任意 HTTP 方法的请求。
fastify.all('/proxy', (req, reply) => {
reply.send({ method: req.method })
})
fastify.register(plugins, opts)
注册插件,是 Fastify 的核心扩展机制。
const fp = require('fastify-plugin')
const prismaPlugin = fp(async (fastify, opts) => {
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
fastify.decorate('prisma', prisma) // 在所有子路由中可用
})
// 注册插件
fastify.register(prismaPlugin)
// 在路由中使用
fastify.get('/users', async (req, reply) => {
return reply.fastify.prisma.user.findMany()
})
请求对象(Request)
fastify.get('/demo/:id', (req, reply) => {
// 路径参数(自动类型转换,:id 转为 number)
req.params.id // '123'(字符串,默认)
// 查询参数
req.query.page // '1'
req.query.filter // 解析后的对象
// 请求体(需配置 bodySchema 或在路由 schema 中定义)
req.body // { name: '张三' }
// 请求头
req.headers['authorization']
req.headers['content-type']
// 原始 Node.js IncomingMessage 对象
req.raw
// 请求 ID(每个请求唯一,用于日志追踪)
req.id // '01ARH3HMS4T1F9G5J3Z...'
// IP 相关
req.ip // '::1' 或 '192.168.1.1'
req.ips // ['::1', '127.0.0.1'](信任代理时)
// 请求协议
req.protocol // 'http' 或 'https'
// 请求方法
req.method // 'GET'
// 请求 URL(不含 host)
req.url // '/users/123?page=1'
// 路由对象
req.routeOptions // { url, method, schema, ... }
// 上下文(注册时传入的数据)
req.context // { appName, ... }
})
响应对象(Reply)
// send — 最核心方法,发送任意数据
reply.send({ data: [] })
reply.send('plain text')
reply.send(Buffer.from('binary'))
// sendFile — 发送文件(需配置 fastify-static)
reply.sendFile('report.pdf')
// sendFile 带文件名(触发下载)
reply.sendFile('report.pdf', {
onHeader: (res, fileName) => {
res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`)
}
})
// json — 发送 JSON(可省略,send 自动识别)
reply.json({ code: 0, data: [] })
reply.status(201).json({ id: 1 }) // 设置状态码 + JSON
// code / status — 设置 HTTP 状态码
reply.code(404).send({ error: 'Not Found' })
reply.status(201)
// redirect — 重定向
reply.redirect('/login')
reply.redirect(301, '/new-path')
reply.redirect('https://external.com')
// header — 设置响应头
reply.header('X-Custom', 'value')
reply.header('Content-Type', 'application/json; charset=utf-8')
// headers — 批量设置
reply.headers({
'X-Powered-By': 'Fastify',
'X-Request-Id': req.id
})
// type — 快捷设置 Content-Type
reply.type('application/json')
// compression
reply.sendCompressed(data) // 发送预压缩数据
// then — Reply 支持 then(用于 async/await)
reply.send({ ok: true })
await reply // 等待响应发送完毕
// 发送空响应
reply.code(204).send()
// 不发送内容
reply.hijack() // 接管响应,不让 Fastify 处理
生命周期钩子
// 全局请求钩子(在所有路由前执行)
fastify.addHook('onRequest', async (req, reply) => {
// 鉴权、IP 白名单等
})
fastify.addHook('preHandler', async (req, reply) => {
// 权限检查、日志等
})
fastify.addHook('preSerialization', async (req, reply, payload) => {
// 修改响应数据(如脱敏)
return payload
})
fastify.addHook('onResponse', async (req, reply) => {
// 记录响应时间
})
fastify.addHook('onTimeout', async (req, reply) => {
// 请求超时处理
})
fastify.addHook('onError', async (req, reply, error) => {
// 全局错误处理
})
// 路由级钩子(仅当前路由生效)
fastify.get('/admin', {
onRequest: [(req, reply, done) => { done() }],
preHandler: [(req, reply, done) => { done() }],
}, handler)
Schema 校验
Fastify 内置基于 JSON Schema 的校验,无需额外依赖。
// 请求参数校验
fastify.post('/users', {
schema: {
body: { // 请求体校验
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', minLength: 2, maxLength: 50 },
email: { type: 'string', format: 'email' },
age: { type: 'integer', minimum: 0, maximum: 150 },
role: { type: 'string', enum: ['admin', 'user'] },
tags: { type: 'array', items: { type: 'string' } },
profile: {
type: 'object',
properties: {
bio: { type: 'string', maxLength: 200 }
}
}
},
additionalProperties: false // 不允许额外字段
},
params: { // 路径参数校验
type: 'object',
required: ['id'],
properties: {
id: { type: 'string', pattern: '^[0-9]+$' }
}
},
querystring: { // 查询参数校验
type: 'object',
properties: {
page: { type: 'integer', minimum: 1, default: 1 },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 20 },
sort: { type: 'string', enum: ['name', '-name', 'created'] }
}
},
response: { // 响应校验(开发辅助,生产可关闭)
200: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string' }
}
}
}
}
}, handler)
插件(Plugin)
const fp = require('fastify-plugin')
// 普通插件
fastify.register(async (fastify, opts) => {
fastify.get('/plugin', (req, reply) => reply.send({ plugin: true }))
})
// 封装插件(外层无法访问内部装饰器)
fastify.register(fp(async (fastify, opts) => {
fastify.decorate('greet', (name) => `Hello, ${name}`)
}, { name: 'greeting' }))
// 在路由中使用插件内装饰器
fastify.get('/hello', (req, reply) => {
reply.send({ message: reply.fastify.greet('World') })
})
装饰器(Decorator)
在 Fastify 实例上挂载自定义属性或方法。
// 添加属性
fastify.decorate('appName', 'MyApp')
// 添加方法
fastify.decorate('utils', {
formatDate: (date) => date.toISOString(),
hashPassword: (pwd) => crypto.createHash('sha256').update(pwd).digest('hex')
})
// 路由中使用
fastify.get('/date', (req, reply) => {
reply.send({ date: fastify.utils.formatDate(new Date()) })
})
// 请求级装饰器(仅当前请求可用)
fastify.register(async (fastify) => {
fastify.addHook('onRequest', async (req, reply) => {
req.customData = { start: Date.now() }
})
})
启动与关闭
// 启动服务
const start = async () => {
try {
await fastify.listen({ port: 3000, host: '0.0.0.0' })
// 等同于 fastify.listen(3000)
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}
start()
// 关闭服务(优雅退出)
const close = async () => {
await fastify.close()
process.exit(0)
}
process.on('SIGINT', close)
process.on('SIGTERM', close)
// 带 SSL 的启动
const fs = require('fs')
await fastify.listen({
port: 3443,
https: {
key: fs.readFileSync('./key.pem'),
cert: fs.readFileSync('./cert.pem')
}
})
错误处理
// 主动抛出错误
fastify.get('/user/:id', async (req, reply) => {
const user = await db.findUser(req.params.id)
if (!user) {
throw reply.notFound() // 404
}
return user
})
// 错误处理钩子
fastify.setErrorHandler((error, req, reply) => {
// validation 校验错误
if (error.validation) {
return reply.status(400).send({
error: 'Validation Error',
details: error.validation
})
}
// 其他错误
fastify.log.error(error)
reply.status(error.statusCode || 500).send({
error: error.message
})
})
// 404 处理
fastify.setNotFoundHandler((req, reply) => {
reply.status(404).send({ error: 'Route not found' })
})
常用配置
// 完整配置示例
const fastify = require('fastify')({
logger: {
level: 'info',
transport: {
target: 'pino-pretty',
options: { colorize: true }
}
},
trustProxy: true, // 信任代理(获取真实 IP)
disableRequestLogging: false, // 关闭自动请求日志
requestIdHeader: 'x-request-id', // 自定义请求 ID 头
requestIdLogLabel: 'requestId',
bodyLimit: 1048576, // 请求体大小限制,默认 1MB
onProtoPoisoning: 'remove', // 原型污染防护
onConstructorPoisoning: 'remove',
ajv: { // 自定义 AJV 实例
customOptions: {
removeAdditional: true,
coerceData: true,
useDefaults: true
}
}
})
// 运行时修改配置
fastify.setContentTypeParser('application/xml', (req, done) => {
let data = ''
req.on('data', chunk => { data += chunk })
req.on('end', () => done(null, data))
})
典型项目结构
src/
app.js // 入口
plugins/ // 插件
prisma.js
auth.js
routes/ // 路由
users.js
articles.js
schemas/ // JSON Schema(可分离)
user.js
services/ // 业务逻辑
userService.js
utils/ // 工具
// app.js
const fastify = require('fastify')({ logger: true })
// 注册插件
fastify.register(require('./plugins/prisma'))
fastify.register(require('./plugins/auth'))
// 注册路由
fastify.register(require('./routes/users'), { prefix: '/api/users' })
fastify.register(require('./routes/articles'), { prefix: '/api/articles' })
// 错误处理
fastify.setErrorHandler((error, req, reply) => {
if (error.validation) {
return reply.status(400).send({ errors: error.validation })
}
reply.status(error.statusCode || 500).send({ error: error.message })
})
fastify.listen({ port: 3000 })
常见问题
| 问题 | 解决方案 |
|---|---|
| 请求体校验失败 400 | 路由缺少 schema 定义,或 schema 定义与实际请求不匹配 |
| 插件中装饰器不可用 | 用 fastify-plugin 包装插件 |
| 想在路由中用异步数据库连接 | 在插件 await fastify.register() 后再启动 listen |
| 日志过多影响性能 | logger: false 或 disableRequestLogging: true |
| JSON Schema 太冗长 | 提取到单独文件 schemas/user.js,然后 import from './schemas/user' |
| 跨域 | fastify.register(require('@fastify/cors'), { origin: true }) |