如何在Express GraphQL的两个resolver函数之间共享数据
解决方案
核心实现思路
你需要的是API调用结果的缓存复用,对应Express session的单请求/单用户维度存储效果,可通过缓存容器+自定义key的方式实现,以下是两种适用不同场景的落地方法:
方案1:请求级缓存(和Express session效果完全一致)
该方案的缓存仅在单次请求内生效,不同用户、不同请求的缓存完全隔离,不会出现数据串用问题,也是你需求的首选方案。
第一步:初始化请求上下文缓存
在GraphQL服务的context配置中,给每个请求生成独立的缓存容器(context本身就是每次请求初始化的,符合session的隔离特性):
// 以Apollo Server为例,其他GraphQL框架的context配置逻辑一致 const server = new ApolloServer({ typeDefs, resolvers, // 每个请求触发一次该函数,生成独立的context实例 context: ({ req }) => { return { // 若需要按用户隔离,可从req中取用户id放到context,缓存key加上用户id即可 userId: req.session?.userId, requestCache: new Map() } } })
第二步:改造resolver实现缓存读写
注意resolver的参数顺序是固定的:(父级返回值, 请求参数, 上下文, 元信息),你之前context没生效大概率是参数解构错误:
const resolvers={ getStudent:async(_, { id }, { requestCache, userId })=>{ // 按用户+学生id生成唯一缓存key,完全对齐session的用户隔离逻辑 const cacheKey = `student:${userId}:${id}` // 缓存命中直接返回,不重复调API if(requestCache.has(cacheKey)){ return requestCache.get(cacheKey) } const resp = await service(id) // 调用API后存入缓存 requestCache.set(cacheKey, resp) return resp }, courseDetails:async(_, { studentId }, { requestCache, userId })=>{ const cacheKey = `student:${userId}:${studentId}` let studentResp = requestCache.get(cacheKey) // 兜底逻辑:如果当前请求还没调用过getStudent,仅触发一次API调用 if(!studentResp){ studentResp = await service(studentId) requestCache.set(cacheKey, studentResp) } // 此处可直接使用拿到的resp,不会重复调用API console.log(studentResp) // 后续业务逻辑 } }
方案2:全局缓存(跨请求复用公共数据)
如果多个用户会查询同一个学生的信息,可使用全局LRU缓存减少跨请求的重复API调用,注意设置缓存上限和过期时间避免内存泄漏:
// 全局初始化LRU缓存,也可替换为Redis实现分布式缓存 const LRU = require('lru-cache') const globalCache = new LRU({ max: 1000, // 最多缓存1000条数据 ttl: 1000 * 60 * 5 // 缓存5分钟过期 }) const resolvers={ getStudent:async(_, { id })=>{ const cacheKey = `student:${id}` if(globalCache.has(cacheKey)){ return globalCache.get(cacheKey) } const resp = await service(id) globalCache.set(cacheKey, resp) return resp }, courseDetails:async(_, { studentId })=>{ const cacheKey = `student:${studentId}` let studentResp = globalCache.get(cacheKey) || await service(studentId) globalCache.set(cacheKey, studentResp) console.log(studentResp) } }
之前context失效的常见原因
- 没有正确获取resolver的第三个context参数,错误地把第一个父级参数当成了请求参数解构
- 没有在服务启动时给context初始化缓存容器,默认context是空对象,无法存储数据
- 混淆了缓存生效范围:请求级context仅在单次请求内有效,跨请求复用需要用全局缓存
内容的提问来源于stack exchange,提问作者Min Yoongi
相关产品推荐
相关产品推荐

