You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Express与GraphQL/Apollo服务器间共享并修改变量?

解决方案

核心思路:共享单例状态对象

问题根源在于你可能误操作了对象引用,或者没有使用真正的全局单例对象。Node.js模块系统默认是单例的,我们可以把会话跟踪对象抽成独立模块,让Express和GraphQL解析器共享同一个引用,这样两边的修改会即时同步。


步骤1:创建独立的会话跟踪模块

新建session-tracker.js文件,导出全局跟踪对象并添加过期清理逻辑:

// session-tracker.js
const userSessionTracker = {};
const TEN_MINUTES = 10 * 60 * 1000;

// 定时清理过期会话
setInterval(() => {
  const now = Date.now();
  for (const userId of Object.keys(userSessionTracker)) {
    if (now - userSessionTracker[userId] > TEN_MINUTES) {
      // 执行登出逻辑:比如清除Keycloak/Passport会话
      delete userSessionTracker[userId];
    }
  }
}, 60 * 1000); // 每分钟检查一次

module.exports = userSessionTracker;

步骤2:在Express中使用该模块

在登录路由或认证中间件里更新会话时间,同时添加过期检查:

// Express 初始化文件
const userSessionTracker = require('./session-tracker');

// 登录成功后更新活跃时间
app.post('/login', (req, res) => {
  const userId = req.user.id;
  userSessionTracker[userId] = Date.now();
  // ...其他登录逻辑
});

// 全局中间件检查会话是否过期
app.use((req, res, next) => {
  if (req.user) {
    const userId = req.user.id;
    const lastActive = userSessionTracker[userId];
    if (lastActive && Date.now() - lastActive > TEN_MINUTES) {
      // 强制登出:清除Passport会话、返回401等
      req.logout();
      return res.status(401).send('会话已过期');
    }
  }
  next();
});

步骤3:在GraphQL解析器中使用该模块

不需要通过context传递跟踪对象,直接在解析器里引入模块并更新时间:

// Apollo Server 初始化文件
const userSessionTracker = require('./session-tracker');
const { ApolloServer } = require('apollo-server-express');

const server = new ApolloServer({
  typeDefs,
  resolvers: {
    Query: {
      getUserData: (_, __, context) => {
        const userId = context.user.id;
        // 更新最后活跃时间
        userSessionTracker[userId] = Date.now();
        // ...你的查询逻辑
      }
    },
    Mutation: {
      updateUser: (_, args, context) => {
        const userId = context.user.id;
        userSessionTracker[userId] = Date.now();
        // ...你的变更逻辑
      }
    }
  },
  context: ({ req }) => {
    // 仅传递用户信息,无需传跟踪对象
    return { user: req.user };
  }
});

为什么之前的方法失效?

  1. 错误的对象赋值方式:如果你的解析器里是重新赋值context.userSessionTracker(比如context.userSessionTracker = {...}),这会替换context里的对象引用,而非修改原全局对象的属性,导致Express端看不到更新。正确做法是直接修改对象属性(context.userSessionTracker[userId] = Date.now())。
  2. Apollo Context的限制误解:文档中“不能破坏性修改contextValue”指的是不要修改context本身的结构(比如添加/删除context属性、重新赋值context),而非禁止修改context中引用对象的属性。修改共享对象的属性不属于破坏性修改。

内容的提问来源于stack exchange,提问作者Quan Nguyen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 03:35:02