Pass HTTP request Context to GraphQL Handler
October 10, 2016
Background
Go’s “net/http” package provides great out of the box server capabilites with extensible middleware. In order to leverage http middleware in combination with the “github.com/graphql-go/graphql” package, we needed to pass the http.Request.Context() to the graphql handler. Here is how we did so:
Code
1 package main
2
3 import (
4 "encoding/json"
5 "net/http"
6
7 "github.com/graphql-go/graphql"
8 "github.com/graphql-go/handler"
9 "github.com/jinzhu/gorm"
10 _ "github.com/jinzhu/gorm/dialects/postgres"
11 )
12
13 func main() {
14 setupServer()
15 }
16
17 func setupMux() *http.ServeMux {
18 mux := http.NewServeMux()
19
20 // graphql Handler
21 graphqlHandler := http.HandlerFunc(graphqlHandlerFunc)
22
23 // add in addContext middlware
24 mux.Handle("/graphql", addContext(graphqlHandler))
25
26 return mux
27 }
28
29 func setupServer() {
30 http.ListenAndServe(":8080", setupMux())
31 }
32
33 func graphqlHandlerFunc(w http.ResponseWriter, r *http.Request) {
34 // get query
35 opts := handler.NewRequestOptions(r)
36
37 // execute graphql query
38 params := graphql.Params{
39 Schema: Schema, // defined in another file
40 RequestString: opts.Query,
41 VariableValues: opts.Variables,
42 OperationName: opts.OperationName,
43 Context: r.Context(),
44 }
45 result := graphql.Do(params)
46
47 // output JSON
48 var buff []byte
49 w.WriteHeader(http.StatusOK)
50 if prettyPrintGraphQL {
51 buff, _ = json.MarshalIndent(result, "", "\t")
52 } else {
53 buff, _ = json.Marshal(result)
54 }
55 w.Write(buff)
56 }
57
58 // Key type is not exported to prevent collisions with context keys defined in
59 // other packages.
60 type key int
61
62 // userAuthKey is the context key for our added struct. Its value of zero is
63 // arbitrary. If this package defined other context keys, they would have
64 // different integer values.
65 const userAuthKey key = 0
66
67 // add values to context
68 func addContext(next http.Handler) http.Handler {
69 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
70 authContext := map[string]interface{}{
71 "key1": 23,
72 "key2": "another value",
73 }
74 ctx := context.WithValue(r.Context(), userAuthKey, authContext)
75 next.ServeHTTP(w, r.WithContext(ctx))
76 })
77 }
Explanation
The AddContext() function is taking the standard http.Request.Context() and supplementing it with our additonal “authContext” map. This Context is then passed to the graphqlHandlerFunc where we setup a new GraphQL instance with the Context initialized to http.Request.Context().
This way, we can access these values within our GraphQL objects.