Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Lessons Learned from Building a REST API on Google App Engine

GolangDC Google App Engine lightning talk presented October 29, 2015

  • Be the first to comment

Lessons Learned from Building a REST API on Google App Engine

  1. 1. Lessons Learned from Building a REST API on Google App Engine Jonathan Altman Presentation to GolangDC-October 29, 2015
  2. 2. Whitenoise Market Webapp • White Noise by TMSoft ( is the leading sleeping app for iOS,Android, Mac, and Windows • Customer wanted a way to: • Allow users to download additional content to the app • Create a vibrant community for users to interact with each other • Scale to the large demand of existing users
  3. 3. White Noise Market App
  4. 4. Project • Build a RESTful API to drive Whitenoise Market’s web front-end • Angular SPA front end, also built as part of the project • User authentication with Google or Facebook account—OAuth2 • Role-based authorization • Implied: customer will use the API from a native mobile client as well • Golang on Google App Engine, leverage their APIs
  5. 5. Sample Calls • GET /api/items — get all items • GET /api/item/item_id — get data about the item with id item_id
  6. 6. GAE via Golang • Project was approx. 6 person/weeks 2nd 1/2 2014, including front end • Customer specification based on their research • Inherited solid proof of concept app, but no firm API • GAE golang support was still beta, long term support indeterminate • Actual GAE API usage calls: outside the scope of this talk (but see
  7. 7. Issues • Package management • Routing • REST response formulation/error logging • OAuth2 support for providers other than Google • Authorization • Miscellaneous
  8. 8. Package Management • goapp get not go get • Not building an exe locally, packages need to be in source tree uploaded to GAE - feels weird compared to golang philosophy
  9. 9. Routing — GAE has choices • Prefix hostname with module — exposing internals • Dispatch file: dispatch.yaml — 10 routing rules max • Roll your own — just start matching URLs in the main dispatch handler in your golang code • or… • and remember: Google Cloud Endpoints were not yet a thing. Probably the way to go today
  10. 10. RollYour Own Router
  11. 11. 3rd Party Router: Gorilla mux! • • Gorilla web toolkit has a bunch of other nice parts • Other 3rd party router libraries probably work fine • Parameterization, method control • GAE takes care of a lot of other things Gorilla toolkit provides r.HandleFunc("/api/comments/{sid}",  handleGetComments).Methods("GET")
 r.HandleFunc(“/api/comments/{sid}",  aihttphelper.AuthenticatedEndpoint(HandleAddComment)).Methods("PUT")
  12. 12. REST Status/Response Logging • Standard REST success and error responses • gorca — • gorca.LogAndMessage: Logs console message and returns short message plus status code • gorca.WriteJSON: succesful responses gorca.LogAndMessage(c,  w,  r,  err,  "error",  "not_authenticated",  http.StatusUnauthorized)   gorca.LogAndMessage(c,  w,  r,  err,  "error",  err.Error(),  http.StatusBadRequest)   gorca.WriteJSON(c,  w,  r,  map[string]interface{}{“status”:  "OK",  "tagAdded":  tagValue})  
  13. 13. OAuth2 Support - gomniauth • GAE does OAuth2 authentication…only for Google • gomniauth does OAuth2 authentication for multiple providers, including google ( • jwt for HTTP Bearer Token — ( • Accepted pull request in gomniauth allows setting http Transport used because the GAE runtime replaces net/http’s DefaultTransport with a context-based one
  14. 14. gomniauth Patch • You have to fetch a Transport with the current requests’ GAE context, and pass that to gomniauth before doing authentication • See 3e2e23995b035e26bbd58a0f56cb2b2d61dbe993 for details/usage
  15. 15. Authorization • Separate from authentication. What a user can do, once we know who the user is • Wrapper function shown before: • “Middleware” takes a target function with an extra argument beyond the normal HTTP request handler for the authenticated user information, and returns a normal HTTP handler function that does the authorization check and runs the target function if authorized • Factory functions encapsulated role info, but could pass in ACL data r.HandleFunc(“/api/comments/{sid}",  aihttphelper.AuthenticatedEndpoint(HandleAddComment)).Methods("PUT")
  16. 16. Authorization Middlewaretype  AiHandlerFunc  func(appengine.Context,  http.ResponseWriter,  *http.Request,  *aitypes.AIUserInfo)   func  generateAuthenticatedEndpoint(h  AiHandlerFunc,  requiredRoles  aitypes.RoleValue)  http.HandlerFunc  {
   return  func(w  http.ResponseWriter,  r  *http.Request)  {
     c  :=  appengine.NewContext(r)
     authUser,  err  :=  AuthenticateRequest(c,  r)
     if  (err  !=  nil)  {
       gorca.LogAndFailed(c,  w,  r,  err)
     //  401  User  not  authenticated     if  (authUser  ==  nil)  {
       http.Error(w,  "",  http.StatusUnauthorized)
     //  403  User  not  authorized  (authenticated,  but  no  permission  to  resource)
     if  (requiredRoles  >  0  &&  !(hasRole(authUser,  requiredRoles))  {
       http.Error(w,  "",  http.StatusForbidden)
     //  User  is  authenticated  and  authorized
     h(c,  w,  r,  authUser)
 }   func  AuthenticatedEndpoint(h  WnHandlerFunc)  http.HandlerFunc  {
   return  generateAuthenticatedEndpoint(h,  0)
  17. 17. Miscellaneous • Concurrency: ignored as a premature optimization. Issues with urlfetch.Transport led to concern on runtime support/research time • GAE API deprecation: not golang specific, but several APIs in use were deprecated post-project and had to be replaced (blobstore) • GAE appears to be going to more of an a la carte model where existing components are replaced with general GCE equivalents • Google Cloud Endpoints were not available at the time
  18. 18. Miscellaneous, cont. • You’ll be playing with the JSON serialization properties. Javascript<- >go naming rules mismatch: nobody wants Javascript properties to begin with capital letters. Also, I tend to prefer map[string]interface{} over defined structs where I can • Using appengine.Context. You will need to, almost everywhere, whether it’s for working with datastore, making outbound http requests, or logging via its .Infof() call
  19. 19. ThankYou! email: github: jonathana twitter: @async_io