Apollo TypePolicies最佳实践:客户端字段依赖缺失问题及解决方案
实现集中式客户端专属字段的类型安全解决方案
原场景与问题
服务端Schema
type Name { first: String last: String } type User { name: Name }
客户端扩展Schema
extend type User { fullName: String! }
TypePolicies实现
import { StrictTypedTypePolicies } from '@/__generated__/apollo-helpers'; // @graphql-codegen/apollo-helpers import { Name } from '@/__generated__/graphql'; export const userTypePolicies: StrictTypedTypePolicies = { User: { fields: { fullName(_, { readField }) { const name = readField<Name>('name'); return `${name?.first} ${name?.last}`.trim(); }, }, }, };
查询语句
user { id fullName @client }
核心问题
使用普通工具函数(如getFullName(user))时具备类型安全性,但仅查询fullName @client时,无法保证同时查询依赖的name { first, last }字段,会导致自定义TypePolicies逻辑失效,只能通过运行时检查readField('name')的存在来规避错误。
解决方案:自定义GraphQL代码生成插件
通过自定义代码生成插件,引入@depends指令声明客户端字段的依赖片段,自动将依赖字段注入查询语句中。
调整后的客户端Schema
directive @depends(fragment: String) on FIELD_DEFINITION fragment User_fullName on User { firstName lastName } extend type User { fullName: String! @depends(fragment: "User_fullName") }
原查询语句
query ($id: ID!) { user(id: $id) { id fullName @client } }
调整后的TypePolicies
import { StrictTypedTypePolicies } from '@/__generated__/apollo-helpers'; import { User_fullNameFragmentDoc } from '@/__generated__/graphql'; export const userTypePolicies: StrictTypedTypePolicies = { User: { fields: { fullName(ref, { cache }) { const current = cache.readFragment({ id: ref as string, fragment: User_fullNameFragmentDoc, }); return `${current?.firstName} ${current?.lastName}`; }, }, }, };
插件生成的最终查询
query UserQuery ($id: ID!) { user(id: $id) { id fullName @client ...User_fullName } } fragment User_fullName on User { firstName lastName }
方案效果
该插件作为documentsTransform使用,能够自动确保客户端字段的依赖服务端字段被包含在查询中,从根源上避免了运行时依赖缺失的问题,实现了客户端专属字段的类型安全,实际使用效果良好。
内容的提问来源于stack exchange,提问作者Anton S
相关产品推荐
相关产品推荐

