使用上下文来共享数据
在不过度加载@keys的情况下,沿着类型层次结构共享数据
该@context
和@fromContext
是指令,属于Apollo企业功能的ApolloRouter并且需要拥有一个
GraphOS Enterprise计划。如果你的组织没有企业计划,你可以通过注册一个免费的
为了让后代能访问祖先的 字段,你可以将其添加为 @key
字段到链中的每个 实体,但这可能会造成问题。deeply nested types 可能会改变许多实体的 @key
字段。添加的字段可能与其添加到的实体无关。最重要的是,过度使用 @key
字段经常打破不同 子图 之间的关注点分离。
使用 @context
和 @fromContext
指令,可以允许一个 子图 在不加载 @key
字段的情况下共享 字段。您可以使用这些指令在一个 子图 中定义一个或多个 上下文。上下文提供了子图在嵌套类型层次结构中共享数据的方式,同时不会过度使用实体键中的无关字段。上下文还保持了不同子图之间的关注点分离。
使用一个上下文
以下是一个单一上下文的示例,下面的 子图 跟踪每个用户最后一次金融交易。 Transaction
类型是 User
类型的子类。每个交易都依赖于相关用户的货币来计算交易额。这种依赖性— currencyCode
参数—取决于用户的货币 userCurrency { isoCode } 的一User
— 是通过上下文定义的。在 User
上的 @context
指令设置上下文,在 currencyCode
上的 @fromContext
获取上下文数据。
scalar CurrencyCode;type Currency {id: ID!isoCode: CurrencyCode!}type User @key(fields: "id") @context(name: "userContext") {id: ID!lastTransaction: Transaction!userCurrency: Currency!}type Transaction @key(fields: "id") {id: ID!currency: Currency!amount: Int!amountInUserCurrency(currencyCode: CurrencyCode@fromContext(field: "$userContext { userCurrency { isoCode } }")): Int!}
ⓘ 注意
- 上下文名称不能包含下划线。
- 在上面的示例中,
userContext
是一个有效的上下文名称,但user_context
就不是。
- 在上面的示例中,
- @fromContext 的参数在API架构中不存在。相反,它由路由器自动填充。
- 在上面的示例中,参数
currencyCode: CurrencyCode!
不会出现在API架构中。
- 在上面的示例中,参数
在 @fromContext 中使用类型条件
在这个示例中,注意@fromContext
指令是如何使用一系列类型条件来选择访问Child.prop1
时的所需字段。如果没有所有可能的上下文都有一个字段,那么就不需要类型条件,例如Child.prop2
的情况。
type Query {a: A!b: B!c: C!}type A @key(fields: "id") @context(name: "context1"){id: ID!field: String!someField: String!child: Child!}type B @key(fields: "id") @context(name: "context1"){id: ID!field: String!someField: String!child: Child!}type C @key(fields: "id") @context(name: "context1") {id: ID!field: String!someOtherField: String!child: Child!}type Child @key(fields: "id") {id: ID!prop1(arg: String!@fromContext(field: "$context1 ... on A { someField } ... on B { someField } ... on C { someOtherField }")): Int!prop2(arg: String!@fromContext(field: "$context1 { field }")): Int!}
如果相同上下文值设置在多个位置——就像示例中的Child.prop1
和Child.prop2
参数——那么
FieldValue
必须将每个位置的所有类型解析为单个与参数类型匹配的值。
ⓘ 注意
联接不保证如果字段可通过多个上下文访问,将使用哪个上下文。
区分上下文
当类型层次结构中的多个祖先实体可能满足一组上下文时,会选择最近的祖先。例如,如果类型的父亲和祖父母都能提供上下文的值,则选择父辈,因为它比父辈更接近。
在以下示例中,给定嵌套类型A
,B
和C
,其中C
引用的上下文可以是A
或B
提供,C
使用B
的值,因为它是比A
更近的祖先:
type Query {a: A!}type A @key(fields: "id") @context(name: "context1") {id: ID!field: String!b: B!}type B @key(fields: "id") @context(name: "context1") {id: ID!field: String!c: C!}type C @key(fields: "id") {id: ID!prop(arg: String! @fromContext(field: "$context1 { field }")): Int!}
在一个更复杂的图形中,一个字段可以通过多个路径访问,并且可以使用不同的字段根据使用的路径来解析prop
。
跨子图引用字段
上下文作用域的定义只能存在于一个 子图模式中。 @fromContext
指令不能引用其他 @context
子图中定义的上下文。然而,您可以使用上下文通过 @external
引用在不同子图间共享数据。
以重用 Transaction
示例 为例,想象一个用于处理 User
和 Currency
类型的 子图
:
scalar CurrencyCodetype Currency @shareable {id: ID!isoCode: CurrencyCode!}type User @key(fields: "id") {id: ID!userCurrency: Currency!}
如果您想从另一个子图中引用这些 字段,您可以使用 @external
指令在子图边界传递数据:
scalar CurrencyCodetype Currency @shareable {id: ID!isoCode: CurrencyCode!}type User @key(fields: "id") @context(name: "userContext") {id: ID!# This is a reference to the field resolved elsewhereuserCurrency: Currency! @external# We add this field to our type herelastTransaction: Transaction!}type Transaction @key(fields: "id") {id: ID!currency: Currency!amount: Int!amountInUserCurrency(currencyCode: CurrencyCode@fromContext(field: "$userContext { userCurrency { isoCode } }")): Int!}