Fastify调用jwt.sign报undefined读取sign属性错误排查
Fastify JWT 生成器
sign 方法未定义报错修复方案 问题描述
基于Fastify规范开发后端JWT生成器时,运行抛出如下错误:
Cannot read properties of undefined (reading 'sign')
错误根因
报错由两个核心问题导致:
- Fastify实例重复初始化,上下文不互通:你在JWT工具函数文件中单独执行
const fastify = require('fastify')()创建了一个全新的Fastify实例,这个实例从未注册过@fastify/jwt插件,本身不存在jwt属性,调用.sign方法时自然会读到undefined。而你在main.ts中注册JWT插件用的是名为app的另一个独立Fastify实例,两个实例的插件、装饰器完全不共享。 - 插件注册存在时序隐患:Fastify的
registerAPI是异步执行的,你当前写法没有等待插件加载完成就注册路由、启动服务,即使实例统一,也可能出现插件未初始化完成就调用JWT方法的问题。
修复方案
1. 删除重复初始化的Fastify实例,统一上下文
删掉工具文件中单独创建Fastify实例的代码,将注册了所有插件的主Fastify实例作为参数传入JWT生成函数:
// 移除这行重复初始化的错误代码 // const fastify = require('fastify')() import type { FastifyInstance } from 'fastify' import { randomBytes, createHash } from "crypto"; import { users } from ".prisma/client"; // 未使用的导入(比如单独导入的jsonwebtoken类型、node-fetch等)可以一并清理 // 新增Fastify实例作为入参 function generateJWTToken(fastify: FastifyInstance, user: users) { const { password, sal, active, ...enviar } = user; const token = fastify.jwt.sign({ ...enviar, // 可主动配置过期时间,例如 { expiresIn: '2h' } }); const decoded = fastify.jwt.decode<{ exp: number }>(token, { complete: true }); return { token, uuid: user.id, nombre: `${user.name} ${user.lastname_p} ${user.lastname_m}`, exp: decoded.payload.exp, }; }
在路由处理逻辑中调用该函数时,直接传入当前请求上下文绑定的Fastify实例即可:
// 路由注册示例 authRouterWeb.post('/login', async (request, reply) => { // 省略用户账号密码校验逻辑 const user = await fastify.prisma.users.findUnique(/* 你的查询条件 */) // 传入fastify实例调用生成函数 const authData = generateJWTToken(fastify, user) return reply.send(authData) })
2. 等待插件注册完成再启动服务
修改main.ts中的启动逻辑,所有register调用前加await,确保所有插件、路由加载完成后再启动端口监听,避免时序问题:
async function main() { await app.register(require("@fastify/swagger"), swaggerOptions); await app.register(require('@fastify/jwt'), { secret: process.env.JWT_SECRET || 'supersecret' // 建议优先读取环境变量 }) app.decorate("prisma", prisma); await app.register(require("@fastify/cors"), { origin: "*", methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowedHeaders: ["Content-Type", "Authorization", "Accept"], }); await app.register(authRouterWeb, { prefix: "/api/v1/auth-web", }); app.listen(port, (err, address) => { if (err) { app.log.error(err); process.exit(1); } app.log.info(`server listening on ${address}`); }); }
额外优化建议
- 移除冗余依赖:
@fastify/jwt内部已经封装了jsonwebtoken的全部能力,且自带完整TS类型支持,不需要额外安装jsonwebtoken包、导入相关类型,减少依赖冗余。 - 敏感配置不要硬编码:JWT密钥、服务端口、数据库连接串这类配置统一从环境变量读取,避免敏感信息随代码仓库泄露。
- 统一实例管理:不要在单独的工具文件顶层初始化Fastify、Prisma这类全局服务实例,统一通过Fastify装饰器挂载、参数传递的方式共享实例,从根源上避免多实例不一致的问题。
- 签发JWT时主动配置有效期、签发者等属性,不要依赖默认配置,方便后续做token过期、续签策略调整。
内容的提问来源于stack exchange,提问作者Ernesto Vazquez
相关产品推荐
相关产品推荐

