Boone Putney bio photo

Boone Putney

Software Development
Random Musings
Austin, Texas

HumanPlanet Soleer

Email LinkedIn Github

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.