Bootstrap

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
            }
        }
    }
}

该能力可通过实现,该组件是一款轻量级、高性能的graphql查询计算引擎。

  • 项目地址:https://github.com/graphql-calculator/graphql-calculator

  • 项目介绍: