GraphQL Schema 定义语言中的别名类型

Posted

技术标签:

【中文标题】GraphQL Schema 定义语言中的别名类型【英文标题】:Alias types in GraphQL Schema Definition Language 【发布时间】:2019-11-15 10:50:10 【问题描述】:

我今天在生产中使用了以下 graphql 架构定义:

type BasketPrice 
  amount: Int!
  currency: String!


type BasketItem 
   id: ID!
   price: BasketPrice!


type Basket 
   id: ID!
   items: [BasketItem!]!
   total: BasketPrice!


type Query 
   basket(id: String!): Basket!

我想将 BasketPrice 重命名为 Price,但是这样做会对架构进行重大更改,因为客户端可能会在片段中引用它,例如

fragment Price on BasketPrice 
   amount
   currency


query Basket 
   basket(id: "123") 
      items 
         price 
            ...Price
         
      
      total 
         ...Price
      
   

我曾希望可以为它起别名以实现向后兼容性,例如

type Price 
  amount: Int!
  currency: String!


# Remove after next release.
type alias BasketPrice = Price;

type BasketPrice 
  amount: Int!
  currency: String!


type BasketItem 
   id: ID!
   price: BasketPrice!


type Basket 
   id: ID!
   items: [BasketItem!]!
   total: BasketPrice!


type Query 
   basket(id: String!): Basket!

但这似乎不是一个功能。有没有推荐的方法来安全地重命名 graphql 中的类型而不会导致重大更改?

【问题讨论】:

【参考方案1】:

由于您已经指定的原因,无法重命名类型而不造成重大更改。重命名类型是表面上的改变,而不是功能上的改变,所以没有实际理由这样做。

处理对架构的任何重大更改的最佳方法是在不同的端点上公开新架构,然后将客户端转换为使用新端点,从而有效地为您的 API 实施版本控制。

我能想到的解决此问题的唯一其他方法是为使用旧类型的任何字段创建新字段,例如:

type BasketItem 
   id: ID!
   price: BasketPrice! @ deprecated(reason: "Use itemPrice instead")
   itemPrice: Price!


type Basket 
   id: ID!
   items: [BasketItem!]!
   total: BasketPrice! @ deprecated(reason: "Use basketTotal instead")
   basketTotal: Price!

【讨论】:

我想重命名类型的实际原因是其他开发人员可以理解代码。如果命名无关紧要,我只会调用每种类型 T1、T2、T3 等。我一直认为 gql 优于 REST 的一个好处是您不必对 API 进行版本控制,因为您只需添加新的在不影响使用 @deprecated 字段的客户端的情况下,希望类型名称也是如此。 我并不是要暗示 命名 本身并不重要——它是——但在重命名某事物以传达意义和重命名为了美观之间有一条细线目的。但是,我可以理解,如果您想将 BasketPrice 类型的使用扩展到篮子以外的事物(例如),那么继续将类型命名为 BasketPrice 是不可取的。 使用 GraphQL 可以更轻松地避免版本控制,但是有些更改会破坏更改,如果您想实现这些更改,版本控制实际上是必要的。虽然您不能像弃用字段那样弃用类型,但重命名字段与重命名类型一样具有重大意义。 @riscarrott 更新了答案,因为从技术上讲,还有另一种方法可以在不破坏架构的情况下解决此限制 我认为“没有实际理由重命名事物”是错误的——名称就是文档。名称通常是文档中最重要的部分!这就是为什么“命名事物”是计算机科学中的两个难点之一,这是有很多道理的。【参考方案2】:

我也想要这个,但显然我们不能拥有它。确保名称随着时间的推移反映实际语义对于正在进行的项目非常重要——这是文档中非常重要的一部分!

我发现执行此操作的最佳方法是多步骤,并且相当费力,但至少可以保持兼容性直到以后。它涉及在协议级别使输入字段成为可选,并在应用程序级别强制执行应用程序级别的需求,即在应用程序级别拥有“其中之一”。 (因为我们没有工会。)

input OldThing 
   thingId: ID!


input Referee 
  oldThing: OldThing!

把它改成这样:

input OldThing 
   thingId: ID!


input NewThing 
  newId: ID!


input Referee 
  oldThing: OldThing @ deprecated(reason: "Use newThing instead")
  newThing: NewThing

实际上,所有老客户都会继续工作。您可以更新处理程序代码以始终生成 NewThing,然后使用程序字段解析器将其复制到 oldThing(如果需要)(取决于您使用的框架。)在输入时,您可以更新处理程序以始终翻译收货时旧换新,并且只在代码中使用新的。如果这两个元素都不存在,您还必须手动返回错误。

在某些时候,客户端都将更新,您可以删除已弃用的版本。

【讨论】:

以上是关于GraphQL Schema 定义语言中的别名类型的主要内容,如果未能解决你的问题,请参考以下文章

我无法在 typeDefs 中导入 schema.graphql 文件:找不到以下指针的任何 GraphQL 类型定义:

graphql-code-generator 依据graphql schema自动生成客户端类型定义

Schema Generator 无法确定 DTO 中自定义对象的 GraphQL 输出类型

如何在我的 GraphQL 中为对象中的对象列表定义类型

如何在我的GraphQL中为对象中的对象列表定义类型

在 GraphQL Schema 中定义地图对象的最佳方式?