Boone Putney bio photo

Boone Putney

Software Development
Random Musings
Austin, Texas

HumanPlanet Soleer

Email LinkedIn Github

Background and provide a great starting point for creating your own Relay-compliant GraphQL server with Go. Authorization, however, is left up completely to the developer. Here is how we implemented a /login endpoint and passed the resulting context to the GraphQL Handler.

We’ll leave the handling of these context values within GraphQL to another post…


  1 package main
  3 import (
  4   "encoding/json"
  5   "net/http"
  7   jwt ""
  8   ""
  9   ""
 10   ""
 11   _ ""
 12 )
 14 func main() {
 15   setupServer()
 16 }
 18 func setupMux() *http.ServeMux {
 19   mux := http.NewServeMux()
 21   // graphql Handler
 22   graphqlHandler := http.HandlerFunc(graphqlHandlerFunc)
 24   // login Handler
 25   mux.HandleFunc("/login", loginFunc)
 27   // add in addContext middlware
 28   mux.Handle("/graphql", requireAuth(graphqlHandler))
 30   return mux
 31 }
 33 func setupServer() {
 34   http.ListenAndServe(":8080", setupMux())
 35 }
 37 // graphqlHandlerFunc creates the graphql handler
 38 func graphqlHandlerFunc(w http.ResponseWriter, r *http.Request) {
 39   // get query
 40   opts := handler.NewRequestOptions(r)
 42   // execute graphql query
 43   params := graphql.Params{
 44     Schema:         Schema, // defined in another file
 45     RequestString:  opts.Query,
 46     VariableValues: opts.Variables,
 47     OperationName:  opts.OperationName,
 48     Context:        r.Context(), // pass http.Request.Context() to our graphql object
 49   }
 50   result := graphql.Do(params)
 52   // output JSON
 53   var buff []byte
 54   w.WriteHeader(http.StatusOK)
 55   if prettyPrintGraphQL {
 56     buff, _ = json.MarshalIndent(result, "", "\t")
 57   } else {
 58     buff, _ = json.Marshal(result)
 59   }
 60   w.Write(buff)
 61 }
 63 // type definition for our claims
 64 type Claims struct {
 65 	UserID  uint64 `json:"userID"`
 66 	IsAdmin bool   `json:"isAdmin"`
 67 	jwt.StandardClaims
 68 }
 70 // secret string for signing requests
 71 var jwtSecret = []byte("secret") // make sure you change this to something secure
 73 // key type is not exported to prevent collisions with context keys defined in
 74 // other packages.
 75 type key int
 77 // userAuthKey is the context key for our added struct.  Its value of zero is
 78 // arbitrary.  If this package defined other context keys, they would have
 79 // different integer values.
 80 const userAuthKey key = 0
 82 // validate JWT submitted via Authorization Header and set context claims
 83 func requireAuth(next http.Handler) http.Handler {
 84 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 85 		// extract jwt
 86 		authorizationHeader := r.Header.Get("Authorization")
 87 		authRegex, _ := regexp.Compile("(?:Bearer *)([^ ]+)(?: *)")
 88 		authRegexMatches := authRegex.FindStringSubmatch(authorizationHeader)
 89 		if len(authRegexMatches) != 2 {
 90 			// didn't match valid Authorization header pattern
 91 			httpError(w, "not authorized", http.StatusUnauthorized)
 92 			return
 93 		}
 94 		jwtToken := authRegexMatches[1]
 96 		// parse tokentoken
 97 		token, err := jwt.ParseWithClaims(jwtToken, &Claims{}, func(token *jwt.Token) (interface{}, error) {
 98 			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
 99 				return nil, fmt.Errorf("Unexpected signing method")
100 			}
101 			return jwtSecret, nil
102 		})
103 		if err != nil {
104 			httpError(w, "not authorized", http.StatusUnauthorized)
105 			return
106 		}
108 		// extract claims
109 		claims, ok := token.Claims.(*Claims)
110 		if !ok || !token.Valid {
111 			httpError(w, "not authorized", http.StatusUnauthorized)
112 			return
113 		}
115 		// load userID & isAdmin into context
116 		authContext := struct {
117 			UserID  uint64 `json:"userId"`
118 			IsAdmin bool   `json:"isAdmin"`
119 		}{
120 			claims.UserID,
121 			claims.IsAdmin,
122 		}
123 		ctx := context.WithValue(r.Context(), userAuthKey, authContext)
124 		next.ServeHTTP(w, r.WithContext(ctx))
125 	})
126 }
128 // loginFunc confirms login credentials and creates JWT if valid
129 func loginFunc(w http.ResponseWriter, req *http.Request) {
130 	// get username & password
131 	decoder := json.NewDecoder(req.Body)
132 	requestBody := struct {
133 		Username string `json:"username"`
134 		Password string `json:"password"`
135 	}{}
136 	err := decoder.Decode(&requestBody)
137 	if err != nil {
138 		http.Error(w, err.Error(), http.StatusInternalServerError)
139 		return
140 	}
141 	defer req.Body.Close()
143 	// confirmLogin is up to you to define
144     user, err := confirmLogin(requestBody)
145     if err != nil {
146         http.Error(w, "invalid login", http.StatusUnauthorized)
147 		return
148     }
150 	//generate token
151 	expireToken := time.Now().Add(time.Hour * 1).Unix()
152 	claims := Claims{
153 		user.ID,
154 		user.IsAdmin,
155 		jwt.StandardClaims{
156 			ExpiresAt: expireToken,
157 			Issuer:    "localhost:8080",
158 		},
159 	}
160 	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
161 	signedToken, _ := token.SignedString(jwtSecret)
163 	//output token
164 	tokenResponse := struct {
165 		Token string `json:"token"`
166 	}{signedToken}
167 	json.NewEncoder(w).Encode(tokenResponse)
168 }


The loginFunc() function at /login receives the posted request and extracts a username and password from it. ConfirmLogin() is not outlined above, but is easy enough to implement. In our scenario, we validate the login against our database and return the user or an error. If the login is valid, we create and return the JWT with our encoded custom claim values (UserID & IsAdmin).

When a request is made to the graphql handler, the requireAuth() middleware parses the JWT, and if valid, passes the (UserID & IsAdmin) variables via context.

The passing of context to GraphQL is explained in this post.


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 “” package, we needed to pass the http.Request.Context() to the graphql handler. Here is how we did so:


 1 package main
 3 import (
 4   "encoding/json"
 5   "net/http"
 7   ""
 8   ""
 9   ""
10   _ ""
11 )
13 func main() {
14   setupServer()
15 }
17 func setupMux() *http.ServeMux {
18   mux := http.NewServeMux()
20   // graphql Handler
21   graphqlHandler := http.HandlerFunc(graphqlHandlerFunc)
23   // add in addContext middlware
24   mux.Handle("/graphql", addContext(graphqlHandler))
26   return mux
27 }
29 func setupServer() {
30   http.ListenAndServe(":8080", setupMux())
31 }
33 func graphqlHandlerFunc(w http.ResponseWriter, r *http.Request) {
34   // get query
35   opts := handler.NewRequestOptions(r)
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)
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 }
58 // Key type is not exported to prevent collisions with context keys defined in
59 // other packages.
60 type key int
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
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 }


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.


In continuation of last year’s post, I present my year in review.

Open Source Projects Published

Open Source Projects Contributed to

Start-ups cofounded

Side projects used daily, but not publicly published

  • Task Docket

Favorite books read

Full list of books read and my thoughts here.

  • Brain Maker: The Power of Gut Microbes to Heal and Protect Your Brain–for Life by David Perlmutter
  • In the Plex: How Google Thinks, Works, and Shapes Our Lives by Steven Levy
  • Les Misérables by Victor Hugo
  • Post Office by Charles Bukowski
  • The Signal and the Noise: Why So Many Predictions Fail - But Some Don’t by Nate Silver
  • Trust Me, I’m Lying: Confessions of a Media Manipulator by Ryan Holiday

TV Series Watched

  • Walking Dead - still
  • Game of Thrones - still
  • Parks and Recreation - amazing feel-good series with two or three of the funniest characters ever created.
  • House of cards - addictive, entertaining series, although often unrealistic (hopefully :)
  • Treme - well done portrait of New Orleans culture, with outstanding music.
  • Transparent - Amazon original series exploring modern family, relationships, and sexuality. Josh Pfefferman is the best.

Courses Taken

Fit in some decent continued education in between consulting and building Human Planet. Special thanks to Coursera, for having such quality classes available to ANYONE for FREE.

Programming Languages/Frameworks/Technologies/etc. used

  • Golang
  • React
  • PostgreSQL
  • Neo4j
  • PHP
  • MySQL
  • Drupal
  • Wordpress
  • Python
  • Django
  • Ruby
  • Jekyll
  • JavaScript
  • jQuery
  • SASS
  • CSS
  • HTML
  • AWS [Amazon Web Services]
    • S3
    • EC2
    • CloudFront
    • Route 53
  • Heroku
  • NetSuite
  • Azure

Review Plans for 2015

Be a better open source community member. Publish & Contribute.

Grade: B+

Contributor to 3 open source projects, and publisher of 1 open source project, and several highly trafficed develment blog posts.

Average at least one blog post per week.

Grade: F

Clocked in at 20 posts, although I had a feeling at the time, and am confident in retrospect, that this was a very ambitious number.

Complete at least one side-project.

Grade: A-

Completed the project, and I use it almost daily, but never attempted to bring it to market.

Build one hardware based project up to functional state. Currently thinking something in the mobile/bluetooth/WiFi area.

Grade: F-

Didn’t even start one :)

Network more, especially in the local Austin startup scene.

Grade: C+

Join co-founders network and met with a lot of interesting people, could definitely do more.

Improve time management and planning. Start each day with set goals.

Grade: D+

Improved performance, but definitely sporadic. This is a big area for improvement in the coming year.

Plans for 2016

  • Implement daily planning and time management
  • 25+ Blog posts (setting a more realistic goal this time)
  • Secure angel-funding for Human Planet
  • Beta launch of Human Planet

In continuation of last year’s reading list, here is my second-annual reading list. I finally made the switch over to Goodreads, so I don’t have to do this from memory this time! As usual, there’s an eclectic mix of genres.

In alphabetical order by title…

1Q84 (1Q84, #1-3) by Haruki Murakami

American Lion: Andrew Jackson in the White House by Jon Meacham

American Sniper: The Autobiography of the Most Lethal Sniper in U.S. Military History by Chris Kyle

Brain Maker: The Power of Gut Microbes to Heal and Protect Your Brain–for Life by David Perlmutter

Broken Windows, Broken Business: How the Smallest Remedies Reap the Biggest Rewards by Michael Levine

Crossing to Safety by Wallace Stegner

Dad Is Fat by Jim Gaffigan

Dead Wake: The Last Crossing of the Lusitania by Erik Larson

Freedom by Jonathan Franzen

Go Set a Watchman by Harper Lee

Growth Hacker Marketing: A Primer on the Future of PR, Marketing, and Advertising by Ryan Holiday

Heaven is for Real: A Little Boy’s Astounding Story of His Trip to Heaven and Back by Todd Burpo

Hooked: How to Build Habit-Forming Products by Nir Eyal

Hyperbole and a Half by Allie Brosh

I Am Malala: The Girl Who Stood Up for Education and Was Shot by the Taliban by Malala Yousafzai

In the Garden of Beasts: Love, Terror, and an American Family in Hitler’s Berlin by Erik Larson

In the Plex: How Google Thinks, Works, and Shapes Our Lives by Steven Levy

John Adams by David McCullough

Les Misérables by Victor Hugo

Let’s Explore Diabetes with Owls by David Sedaris

Liar’s Poker by Michael Lewis

Maude by Donna Mabry

Missoula: Rape and the Justice System in a College Town by Jon Krakauer

Not My Father’s Son: A Memoir by Alan Cumming

Pearl Buck’s the Good Earth by Pearl S. Buck

Post Office by Charles Bukowski

Rob Delaney: Mother. Wife. Sister. Human. Warrior. Falcon. Yardstick. Turban. Cabbage. by Rob Delaney

Shakespeare Saved My Life: Ten Years in Solitary with the Bard by Laura Bates

Steve Jobs by Walter Isaacson

Tales of the Jazz Age (F. Scott Fitzgerald Classic) by F. Scott Fitzgerald

The Art of Living: The Classical Manual on Virtue, Happiness and Effectiveness by Epictetus

The Atlantis Gene (The Origin Mystery, #1) by A.G. Riddle

The Atlantis Plague (The Origin Mystery, #2) by A.G. Riddle

The Atlantis World (The Origin Mystery, #3) by A.G. Riddle

The Autobiography of Malcolm X by Malcolm X

The Bell Tolls for No One by Charles Bukowski

The Blessing of a Skinned Knee: Using Jewish Teachings to Raise Self-Reliant Children by Wendy Mogel

The Book Thief by Markus Zusak

The Boy Who Harnessed the Wind: Creating Currents of Electricity and Hope by William Kamkwamba

The Corrections by Jonathan Franzen

The Cryptographer’s Way by Dr. Bradford Hardie III

The Grand Design by Stephen Hawking

The Life and Times of the Thunderbolt Kid by Bill Bryson

The Life Changing Magic of Tidying Up by Marie Kondō

The Lost City of Z: A Tale of Deadly Obsession in the Amazon by David Grann

The Martian by Andy Weir

The Miracle Morning: The Not-So-Obvious Secret Guaranteed to Transform Your Life (Before 8AM) by Hal Elrod

The Pearl by John Steinbeck

The Signal and the Noise: Why So Many Predictions Fail - But Some Don’t by Nate Silver

Things Fall Apart (The African Trilogy, #1) by Chinua Achebe

Three Weeks With My Brother by Nicholas Sparks

Trust Me, I’m Lying: Confessions of a Media Manipulator by Ryan Holiday

Twenty Thousand Leagues Under the Sea by Jules Verne

We Are Not Ourselves by Matthew Thomas

When to Rob a Bank by Steven D. Levitt

Where the Red Fern Grows by Wilson Rawls

Wild: From Lost to Found on the Pacific Crest Trail by Cheryl Strayed

Wool by Hugh Howey Omnibus Edition (Books 1-5 of the Silo Series) l Summary & Study Guide by BookRags

Yes Please by Amy Poehler

Zealot: The Life and Times of Jesus of Nazareth by Reza Aslan

Zero to One: Notes on Startups, or How to Build the Future by Peter Thiel

When recently doing some last minute Christmas shopping on Amazon, I was presented with a suggestion to check out the Coin 2.0, with surprisingly positive average customer reviews (4 stars visually). I had been a supporter of the Coin Kickstarter campaign the first go-around, but canceled after numerous delays and poor reviews on the beta versions.

Out of curiosity, I clicked on the link:

At quick glance, I found that all of the featured reviews in the main area and on the sidebar where largely negative. Then I looked at the Customer Reviews graph, and saw that the following breakdown:

  • 5 star: 32%
  • 4 star: 12%
  • 3 star: 7%
  • 2 star: 19%
  • 1 star: 30%

This didn’t look like it averaged out to 4 stars, so I quickly ran the numbers. The actual average was under 3 stars (2.97)!

I did some quick research, and found that Amazon had recently changed their formula to provide more weight to “newer, more helpful and verified customer reviews” link.

Sorting the reviews by both “most helpful” and “most recent”, I found both to largely be weighted towards negative (3 stars or less) as well.

After a quick search, I couldn’t find any specifics on Amazon’s “Machine Learning Algorithm.” I’m not sure what’s going on here, and I’m generally a fan of Amazon, but this seems a little disingenuous. Thoughts?