GraphQL枚举能否返回描述字符串?实现多语言状态展示
我之前在做多语言GraphQL服务的时候碰到过一模一样的问题,给你几个实用的解决方案,你可以根据自己的场景挑:
方案1:给枚举值添加描述字段(Description)
大多数GraphQL实现(比如Apollo Server、GraphQL.js)都支持给枚举值添加@description指令,直接把法语文本写在里面:
enum ProductState { HAS_BEEN_SENT @description("Expédiée") NOT_SENT_YET @description("Non expédiée") RECEIVED @description("Reçue") }
前端可以通过**内省查询(Introspection Query)**一次性拉取所有枚举值和对应的描述,然后在客户端缓存成一个映射对象:
query GetProductStates { __type(name: "ProductState") { enumValues { name description } } }
拿到结果后就能生成类似const stateLabelMap = { HAS_BEEN_SENT: "Expédiée", ... }的映射,之后用枚举值查这个map就能显示正确文本了。这个方案的好处是不需要修改现有查询结构,保留了枚举的类型安全。
方案2:定义状态对象类型代替纯枚举
如果觉得描述字段不够灵活(比如以后要加状态颜色、优先级之类的额外信息),可以直接定义一个包含枚举键和显示文本的对象类型:
# 先保留原来的枚举用于后端逻辑 enum ProductState { HAS_BEEN_SENT NOT_SENT_YET RECEIVED } # 定义给前端用的状态对象类型 type ProductStateDisplay { key: ProductState! # 后端逻辑用的枚举键 label: String! # 法语显示文本 } # 修改产品类型,把state字段换成这个对象类型 type Product { id: ID! name: String! state: ProductStateDisplay! }
然后在Resolver里做映射处理(以Node.js/Apollo为例):
const stateLabelMap = { HAS_BEEN_SENT: "Expédiée", NOT_SENT_YET: "Non expédiée", RECEIVED: "Reçue" }; const Product = { state: (parent) => ({ key: parent.state, label: stateLabelMap[parent.state] }) };
前端查询的时候直接请求state { key label },既能拿到用于逻辑判断的key,又能直接用label显示,非常方便。这个方案是我最推荐的,兼顾了类型安全和前端易用性。
方案3:自定义标量类型(适合特殊场景)
如果完全不需要后端的枚举类型检查,只想让客户端直接拿到法语文本,可以自定义一个标量类型:
scalar ProductStateScalar type Product { id: ID! name: String! state: ProductStateScalar! }
然后实现标量的序列化和解析逻辑,把后端存的枚举值转成法语文本返回给客户端:
const { GraphQLScalarType } = require('graphql'); const { Kind } = require('graphql/language'); const stateMap = { HAS_BEEN_SENT: "Expédiée", NOT_SENT_YET: "Non expédiée", RECEIVED: "Reçue" }; const reverseMap = Object.fromEntries(Object.entries(stateMap).map(([k, v]) => [v, k])); const ProductStateScalar = new GraphQLScalarType({ name: 'ProductStateScalar', description: '产品状态,输出为法语文本,输入支持枚举键或文本', serialize(value) { // 给客户端返回法语文本 return stateMap[value] || value; }, parseValue(value) { // 处理客户端传入的文本,转成后端需要的枚举键 return reverseMap[value] || value; }, parseLiteral(ast) { if (ast.kind === Kind.STRING) { return reverseMap[ast.value] || ast.value; } return null; } });
这个方案的缺点是失去了枚举的类型校验优势,但胜在客户端拿到的直接是显示文本,不用额外处理。
内容的提问来源于stack exchange,提问作者Quentin D




