Node.js中Express-Session结合Redis在TypeScript下无法正常工作
背景
在Node.js后端使用express-session搭配Redis存储时,JavaScript代码运行正常:
const express = require('express'); const session = require('express-session'); const redis = require('redis'); const RedisStore = require('connect-redis')(session); const app = express(); let redisClient = redis.createClient(6380, process.env.REDISCACHEHOSTNAME, {auth_pass: process.env.REDISCACHEKEY, tls: {servername: process.env.REDISCACHEHOSTNAME}}); app.use(session({ store: new RedisStore({ client: redisClient }), }));
升级到TypeScript后,复用原有逻辑出现问题,当前代码如下:
import express = require('express'); import session = require('express-session'); import redis = require('redis'); import connectRedis from 'connect-redis'; const app = express(); const RedisStore = connectRedis(session); const redisClient = redis.createClient({ url: "rediss://" + process.env.REDISCACHEHOSTNAME + ":6380", password: process.env.REDISCACHEKEY, }); redisClient.connect().then(() => { const redisStore = new RedisStore({ client: redisClient }); app.use(session({ store: redisStore })); // ... some app routes });
Redis连接看似成功,但更新会话(如req.session.user = { ... })时出现以下错误:
xxx\backend\node_modules@redis\client\dist\lib\client\RESP2\encoder.js:17
throw new TypeError('Invalid argument type');
^ TypeError: Invalid argument type
at encodeCommand (xxx\backend\node_modules@redis\client\dist\lib\client\RESP2\encoder.js:17:19)
at RedisCommandsQueue.getCommandToSend (xxx\backend\node_modules@redis\client\dist\lib\client\commands-queue.js:187:45)
问题原因与解决方案
核心原因
错误源于Redis客户端版本与connect-redis版本不兼容:
- 原JavaScript代码使用的是Redis v3及以下的回调式客户端,对应旧版
connect-redis(v6及以下); - 现在TypeScript代码中使用的是Redis v4+的Promise式客户端,若安装的
connect-redis是v6及以下版本,它无法适配Redis v4的API,调用命令时参数格式不匹配,导致RESP编码器抛出类型错误。
解决方案1:升级connect-redis以适配Redis v4
安装支持Redis v4的connect-redis版本(v7及以上):
npm install connect-redis@latest # 或 yarn add connect-redis@latest
修改后的TypeScript代码示例:
import express from 'express'; import session from 'express-session'; import { createClient } from 'redis'; import RedisStore from 'connect-redis'; const app = express(); // 创建Redis v4客户端 const redisClient = createClient({ url: `rediss://${process.env.REDISCACHEHOSTNAME}:6380`, password: process.env.REDISCACHEKEY, }); // 监听Redis客户端错误 redisClient.on('error', (err) => console.error('Redis客户端错误:', err)); // 初始化RedisStore const redisStore = new RedisStore({ client: redisClient, prefix: 'app-session:', // 可选:会话键前缀,避免与其他Redis数据冲突 }); // 异步启动服务:先连接Redis,再挂载session中间件 (async () => { await redisClient.connect(); app.use(session({ store: redisStore, secret: 'your-strong-secret-key', // 必须配置:用于会话签名的密钥 resave: false, // 避免无变化时重复存储会话 saveUninitialized: false, // 不存储未初始化的空会话 cookie: { secure: process.env.NODE_ENV === 'production', // 生产环境开启HTTPS Cookie maxAge: 24 * 60 * 60 * 1000, // 会话有效期1天 }, })); // 测试路由:设置会话 app.get('/set-user', (req, res) => { req.session.user = { id: 1, username: 'test-user' }; res.send('用户信息已存入会话'); }); app.listen(3000, () => console.log('服务运行在3000端口')); })();
解决方案2:降级Redis客户端到v3版本
如果不想升级connect-redis,可以将Redis客户端降级到v3.x,写法与原JavaScript逻辑对齐:
npm install redis@3.x # 或 yarn add redis@3.x
修改后的TypeScript代码示例:
import express = require('express'); import session = require('express-session'); import redis = require('redis'); import connectRedis from 'connect-redis'; const app = express(); const RedisStore = connectRedis(session); // 创建Redis v3客户端 const redisClient = redis.createClient({ host: process.env.REDISCACHEHOSTNAME, port: 6380, password: process.env.REDISCACHEKEY, tls: { servername: process.env.REDISCACHEHOSTNAME }, }); redisClient.on('error', (err) => console.error('Redis客户端错误:', err)); app.use(session({ store: new RedisStore({ client: redisClient }), secret: 'your-strong-secret-key', resave: false, saveUninitialized: false, })); // 测试路由 app.get('/set-user', (req, res) => { req.session.user = { id: 1, username: 'test-user' }; res.send('用户信息已存入会话'); }); app.listen(3000, () => console.log('服务运行在3000端口'));
内容的提问来源于stack exchange,提问作者Nico Schuck

