Graphql: Modifying request context in a resolver

Created on 9 Jul 2018  路  2Comments  路  Source: graphql-go/graphql

I'm trying to write a resolver that not only returns fields, but modifies the context, but I can't quite figure out how to do this.

Example:

// ResolveAllResources will process an allResources request
func ResolveAllResources(p graphql.ResolveParams) (interface{}, error) {
    result := map[string]interface{}{
        "totalCount": 1,
        "pageInfo":   dummyPageInfo,
        "edges":      []map[string]interface{}{},
        "nodes":      []map[string]interface{}{},
        "groups":     map[string]interface{}{},
    }

    ctx := context.WithValue(p.Context, "query", map[string]string{"myQuery": "foo"})

    return result, nil
}

I can't see a way to stuff the new context in the request/response from here so that child resolvers can access it.

I'm used to doing this sort of thing in the Lacinia library from Clojure:

(defn resolve-resources-connection
  [{:keys [query] :as context}
   args parent]
  (-> {:pageInfo dummy-page-info
       :nodes []
       :edges []
       :groups {}}
      (with-context (assoc context :query (build-query (:query context) nil args)))))
question

Most helpful comment

Oh, great! Thank you. This works!

Since I expect to use this a lot in my project, I just added a simple helper function to my package:

func getRootValue(p graphql.ResolveParams) map[string]interface{} {
    return p.Info.RootValue.(map[string]interface{})
}

All 2 comments

Hi @robdaemon, thanks a lot for using the lib and reaching us for a question.

Context is meant to be use as per-request and down to the stack. For example the current user logged in.

To pass arbitrary data from top resolvers to children resolvers, we can use the optional field RootValue.

I went ahead and wrote a small working example based on the snippet you shared:

package main

import (
    "fmt"
    "log"

    "github.com/graphql-go/graphql"
)

var EdgeType = graphql.NewObject(graphql.ObjectConfig{
    Name: "Edge",
    Fields: graphql.Fields{
        "node": &graphql.Field{
            Type: graphql.String,
            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                rootValue := p.Info.RootValue.(map[string]interface{})
                fmt.Printf("[node] rootValue: %+v\n", rootValue)
                return nil, nil
            },
        },
    },
})

func ResolveAllResources(p graphql.ResolveParams) (interface{}, error) {
    rootValue := p.Info.RootValue.(map[string]interface{})
    rootValue["query"] = map[string]string{"myQuery": "foo"}
    fmt.Printf("[edge] rootValue: %+v\n", rootValue)
    result := []string{"node1"}
    return result, nil
}

func main() {
    schema, err := graphql.NewSchema(graphql.SchemaConfig{
        Query: graphql.NewObject(graphql.ObjectConfig{
            Name: "Query",
            Fields: graphql.Fields{
                "edges": &graphql.Field{
                    Type:    graphql.NewList(EdgeType),
                    Resolve: ResolveAllResources,
                },
            },
        }),
    })
    if err != nil {
        log.Fatal(err)
    }
    rootObject := make(map[string]interface{})
    graphql.Do(graphql.Params{
        Schema:        schema,
        RequestString: "{ edges { node } }",
        RootObject:    rootObject,
    })
}
(issue-345)-> go run main.go
[edge] rootValue: map[query:map[myQuery:foo]]
[node] rootValue: map[query:map[myQuery:foo]]

Oh, great! Thank you. This works!

Since I expect to use this a lot in my project, I just added a simple helper function to my package:

func getRootValue(p graphql.ResolveParams) map[string]interface{} {
    return p.Info.RootValue.(map[string]interface{})
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

davejohnston picture davejohnston  路  3Comments

conord33 picture conord33  路  6Comments

zbintliff picture zbintliff  路  3Comments

sogko picture sogko  路  6Comments

rustysys-dev picture rustysys-dev  路  6Comments