FSharp.Data.GraphQL icon indicating copy to clipboard operation
FSharp.Data.GraphQL copied to clipboard

[WIP] Uniform way for working with resolver results

Open Horusiath opened this issue 8 years ago • 0 comments

In one of my blog posts I've shown a way - inspired by Twitter's Finagle - of applying additional logic to service calls. I think we could apply something similar here. It's a proposal and needs some more polishing.

Given a resolver function like:

type Resolve<'In, 'Out> = ResolveFieldContext -> 'In -> AsyncVal<'Out>

we could define a filter for it:

type Filter<'In1, 'In2, 'Out2, 'Out1> = 
    Emitter<'Out1> -> Resolve<'In1, 'Out2> -> ResolveFieldContext -> 'In1 -> AsyncVal<unit>
type Emitter<'Out> = 'Out -> unit

What is a filter? It's a wrapper around a resolver, that may apply some additional logic to it. It may (or may not) use emitter to nest value returned from resolver into result set set to user. What we could do with that?:

  • It may be used as a general-purpose mechanism to implement directives. Currently we support only two of them (@skip and @include) and they are handled as special conditions. With filters we could implement directive logic as a filter that will be conditionally compiled into resolution pipeline.
  • With emitters we could implement sequential results i.e. @defer directive.

Some example to visualize:

// filter that could be used for @skip directive
let skipDirective emit resolve ctx input = asyncVal {
    let cond = ctx.Arg "if" 
    if not cond 
    then 
        // resolve a value if @skip(if: true)
        let! output = resolve ctx input
        emit output  // emit output to corresponding key-value result set
}

Horusiath avatar Mar 02 '17 18:03 Horusiath