GraphQL前端返回重复数据但后端正常,求排查解决
前端GraphQL查询返回重复数据的解决方法
问题场景
用GraphQL Sandbox查询MongoDB数据时,能返回3条符合条件的唯一Player数据;但前端通过useQuery调用相同查询,得到的数组里是同一条数据重复3次(数量等于limit设置值)。移除resolver里的id查询条件后,前端能正常获取不同数据,同页面其他集合的类似功能无异常。
相关代码片段:
Resolver
players: async (parent, { id }) => { return Player.find({ id: id }).sort({ game_id: -1 }).limit(3); },
TypeDefs
type Player { _id: ID game_id: Int id: String player_name: String PTS: Int } type Query { players(id: String): [Player] }
前端查询
export const GET_PLAYER = gql` query Players($playersId: String) { players(id: $playersId) { _id game_id player_name PTS } }
问题原因
Apollo Client默认会用对象的id或_id字段作为缓存的唯一标识键。你的Player类型中,业务用的id字段是多个文档共享的值(查询条件就是这个id),导致Apollo Client把所有返回的Player实例都判定为同一个实体,缓存时后面的结果覆盖前面的,最终前端读取缓存时重复返回同一条数据。而Sandbox默认不启用客户端缓存,所以能返回正确结果。
解决方案
方案1:指定Player类型的缓存键为_id
修改Apollo Client的缓存配置,明确告诉它用MongoDB的唯一主键_id作为Player实体的标识:
import { ApolloClient, InMemoryCache } from '@apollo/client'; const client = new ApolloClient({ uri: '/graphql', cache: new InMemoryCache({ typePolicies: { Player: { keyFields: ['_id'], // 强制使用_id作为缓存唯一键 }, }, }), });
方案2:查询时禁用缓存(临时调试用)
在useQuery中设置fetchPolicy为network-only,强制每次从服务器获取数据,不使用缓存:
const { data } = useQuery(GET_PLAYER, { variables: { playersId: '你的目标ID' }, fetchPolicy: 'network-only', });
注意:这个方案适合快速验证问题,但不推荐在生产环境长期使用,会失去缓存带来的性能优势。
方案3:重命名业务用的id字段
把类型定义中业务相关的id字段改名(比如player_id),避免和Apollo默认的缓存键字段冲突:
- 修改TypeDefs:
type Player { _id: ID game_id: Int player_id: String # 原id字段改名 player_name: String PTS: Int } type Query { players(player_id: String): [Player] # 同步修改查询参数名 }
- 修改Resolver:
players: async (parent, { player_id }) => { return Player.find({ id: player_id }).sort({ game_id: -1 }).limit(3); },
- 修改前端查询:
export const GET_PLAYER = gql` query Players($playersId: String) { players(player_id: $playersId) { _id game_id player_name PTS } }
验证
修改后再次调用前端查询,应该能获取到3条不同的Player数据,和Sandbox的结果一致。
内容的提问来源于stack exchange,提问作者evanhu96
相关产品推荐
相关产品推荐

