graphql计算指令之@skipBy和@includeBy:使用表达式实现简单控制流
问题背景
graphql内置了 @skip 和 @include 来决定是否跳过某个字段的解析获取,其参数为bool类型,但是真实的业务场景往往更加复杂,需要表达式计算支持。比如只有使用指定版本app的用户才可以看到某些数据。
如下查询,期望只有v2版本的客户端才可以看到email字段,这种逻辑不论是硬编码在email对应的,还是返回多余的数据让客户端自行忽略,都不够灵活,尤其在单独获取email开销较大时、第二种方案是不可接受的。
query userInfoQuery($userId:Int){
consumer{
userInfo(userId: $userId){
userId
age
name
email
}
}
}
解决方案
graphql提供了指令机制,该机制类似于java注解,可用于graphql查询执行能力的动态拓展。
directive @skipBy(predicate: String!) on FIELD | INLINE_FRAGMENT | FRAGMENT_SPREAD
directive @includeBy(predicate: String!) on FIELD | INLINE_FRAGMENT | FRAGMENT_SPREAD
predicate:判断是否解析该字段的表达式,表达式参数为查询变量;
@skipBy 和 @includeBy可通过表达式判断是否请求该字段。同@include和@skip一样,@includeBy也可定义在片断上,如果predicate计算结果不为bool类型或抛异常,则查询将抛异常,且不会真正执行每个字段的请解析。
使用@includeBy查询如下,只有表达式为true时,email对应的DataFetcher才会执行。
query includeExample($userId:Int,$clientVersion:String){
consumer{
# 受限于graphql原生语法校验,变量$clientVersion必须被明确的作为参数使用
userInfo(
userId: $userId,
clientVersion: $clientVersion
){
userId
age
name
# 只在v2版本的客户端中展示
email @includeBy(predicate: "clientVersion == 'v2'")
}
}
}
当有多个字段需要通过同一表达式判断是否忽略的时候,可通过实现:只有v2版本的用户才可以看到 name 和 email。
query includeExample($userId:Int,$clientVersion:String){
consumer{
userInfo(
userId: $userId,
clientVersion: $clientVersion){
userId
age
...@includeBy(predicate: "clientVersion == 'v2'"){
name
email
}
}
}
}