You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

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

火山引擎 最新活动