FSharp.Data.GraphQL
FSharp.Data.GraphQL copied to clipboard
[WIP] Uniform way for working with resolver results
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 (
@skipand@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
}