Publicidad
Publicidad

Más contenido relacionado

Publicidad
Publicidad

GraphQL IN Golang

  1. GRAPHQL IN GO MODERNWEB 2018
  2. ABOUT ME Open source contributor 1. Gitea 2. Drone 3. Gin appleboy @ GitHub appleboy @ twitter appleboy @ slideshare appleboy46 @ facebook 2
  3. AGENDA ▸ Why we moving API from REST to Graphql? ▸ What is Graphql? ▸ Graphql in Golang (Why we choose Golang) ▸ How to testing Graphql in Golang ▸ Deploy Graphql application
  4. MOVING REST TO GRAPHQL icon from https://bit.ly/2LhOpZA
  5. 1. LARGE REQUEST IN SINGLE PAGE https://bit.ly/2NpS4Fu
  6. 1. LARGE REQUEST IN SINGLE PAGE
  7. CUSTOM FIELDS /api/scene/1?field=name,description,created_at
  8. CUSTOMENDPOINT /api/scene/1/mobile?field=name,description /api/scene/1/web?field=name,description
  9. DOCUMENTATION? HOW TO GENERATE DOCUMENT AUTOMATICALLY? API DOC Swagger
  10. UPDATE API GENERATE DOC BACKEND FRONTEND MOBILETESTING
  11. /** * @api {get} /scene/:id Request Scene information * @apiName GetScene * @apiGroup Scene * * @apiParam {Number} id Scenes unique ID. * * @apiSuccess {String} title Title of the Scene. * @apiSuccess {String} desc Description of the Scene. */
  12. GRAPHQL
  13. WHATISGRAPHQL? IT IS A QUERY LANGUAGE
  14. { myScene { name description } } LEXED PARSED VALIDATED EXECUTED
  15. { scene(id: 1) { name description } } LEXED PARSED VALIDATED EXECUTED
  16. { scene(id: 1) { name description items(type: DEVICE) { id name url } } } Graphql Request
  17. { "scene": { "name": "foo", "description": “bar", "items": [{ "id": 1, "name": "test_1", "url": "http://foo.com" }, { "id": 2, "name": "test_2", "url": "http://bar.com" }] } } JSON Response
  18. /api/scene/1 /api/scene/1/item /api/scene/1/all
  19. TYPE SYSTEM GRAPHQL LANGUAGE
  20. { myScene { name description } }
  21. { scene(id: 1) { name description items(type: DEVICE) { id name url } } }
  22. type QueryRoot { myScene: Scene scene(id: ID!): Scene }
  23. { scene(id: 1) { name description items(type: DEVICE) { id name url } } } Graphql Request
  24. type Scene { name: String! description: String items(type: ItemEnum): [Item] } enum ItemEnum { DEVICE, TEST_DEVICE, URL }
  25. { scene(id: 1) { name description items(type: DEVICE) { id name url } } } Graphql Request
  26. type Item { id: ID! name: String! url: String }
  27. INTROSPECTION GRAPHQL LANGUAGE
  28. { __schema { queryType { name } } }
  29. { "data": { "__schema": { "queryType": { "name": "Query" } } } }
  30. WHY NEED THE INTROSPECTION ▸code generation ▸auto documentation ▸static validation ▸IDE Integration
  31. https://github.com/prismagraphql/graphql-playground
  32. https://www.prisma.io/blog/introducing-graphql-playground-f1e0a018f05d/
  33. RESOLVINGFIELDS GRAPHQL LANGUAGE
  34. type Item { id: ID type: sceneEnum name: String }
  35.     "id": &graphql.Field{       Type: graphql.Int,     }, INT FLOAT STRING BOOLEAN ID
  36. type Item { id: ID type: sceneEnum name: String }
  37.     "TEST_DEVICE": &graphql.EnumValueConfig{       Value: int(model.TypeTestDevice),       Description: "test device.",     },     "DEVICE": &graphql.EnumValueConfig{       Value: int(model.TypeDevice),       Description: "device.",     },     "URL": &graphql.EnumValueConfig{       Value: int(model.TypeURL),       Description: "url.",     },
  38. "name": &graphql.Field{ Type: graphql.String, Resolve: func(p graphql.ResolveParams) (interface{}, error) { o, _ := p.Source.(*model.Scene) return "Hello, " + o.Name, nil }, }, Return Custom Value
  39. MUTATION GRAPHQL LANGUAGE
  40. mutation { createScene( title: "foo", description: "bar" ) { id title description url } }
  41. // Schema is the GraphQL schema served by the server. var Schema, _ = graphql.NewSchema(graphql.SchemaConfig{   Query: rootQuery,   Mutation: rootMutation,   Types: types, }) Query and Mutation
  42. SINGLE ENDPOINT POST /graphql
  43. GRAPHQL IN GOLANG WHY WE CHOOSE GOLANG?
  44. WHY WE CHOOSE GOLANG? ▸Compiles Into Single Binary ▸Static Type System ▸Performance ▸Easy to Deploy ▸Learning Curve
  45. BENCHMARKOFGRAPHQLFRAMEWORKINGOLANG ▸graphql-go/graphql ▸playlyfe/go-graphql ▸graph-gophers/graphql-go ▸samsarahq/thunder
  46. https://github.com/appleboy/golang-graphql-benchmark $ go test -v -bench=. -benchmem
  47. VEKTAH/GQLGEN GO GENERATE BASED GRAPHQL SERVER LIBRARY https://github.com/vektah/gqlgenhttps://youtu.be/3dpqYMqyiOg
  48. API SERVER API SERVER GRAPHQLSERVER GRAPHQL GATEWAY
  49. GRAPHQLINGOLANG PACKAGE IN GOLANG
  50. FRAMEWORKINGO
  51. func Handler() gin.HandlerFunc {   h := handler.New(&handler.Config{     Schema: &schema.Schema,     Pretty: true,   })   return func(c *gin.Context) {     h.ServeHTTP(c.Writer, c.Request)   } } graphql schema
  52. JWT TOKEN SERVER GRAPHQL SERVER
  53. func Check() gin.HandlerFunc {   return func(c *gin.Context) {     token := c.GetHeader("Authorization")     user, err := authUser(token)     if token != "" && err != nil {       logrus.Errorf("authUser error: %s", err.Error())     }     if user != nil && user.Email != "" {       c.Set("email", user.Email)       c.Set("user_id", user.UserID)     }     ctx := context.WithValue(       c.Request.Context(),       config.ContextKeyUser,       user,     )     c.Request = c.Request.WithContext(ctx)   } } store user data
  54. var Schema, _ = graphql.NewSchema(graphql.SchemaConfig{   Query: rootQuery,   Mutation: rootMutation, }) var rootMutation = graphql.NewObject(   graphql.ObjectConfig{     Name: "RootMutation",     Description: "Root Mutation",     Fields: graphql.Fields{       "updateUser": &updateUser,       "createScene": &createScene,       "updateScene": &updateScene,       "deleteScene": &deleteScene,     },   }) var rootQuery = graphql.NewObject(   graphql.ObjectConfig{     Name: "RootQuery",     Description: "Root Query",     Fields: graphql.Fields{       "queryUser": &queryUser,       "searchUser": &searchUser,       "queryScene": &queryScene,     },   }) query mutation
  55. GRAPHQL ERROR BETTER ERROR HANDLING
  56. { "data": { "post": null }, "errors": [ { "message": "Internal Error: 404 not found", "locations": [ { "line": 2, "column": 3 } ], "path": [ "post" ] } ] } single message error location
  57. func (g FormattedError) MarshalJSON() ([]byte, error) {   m := map[string]interface{}{}   for k, v := range g.Extensions {     m[k] = v   }   m["message"] = g.Message   m["locations"] = g.Locations   return json.Marshal(m) } Marshal JSON custom key value
  58. func FormatError(err error, arg ...interface{}) gqlerrors.FormattedError {   switch err := err.(type) {   case gqlerrors.FormattedError:     return err   case *Error:     return gqlerrors.FormattedError{       Message: fmt.Sprintf(err.Error(), arg...),       Extensions: gqlerrors.ErrorExtensions{         "code": err.Type.Code(),         "type": err.Type,       },     } } custom error original error
  59. GRAPHQL N+1 DATA LOADER
  60. 1. LARGE REQUEST IN SINGLE PAGE
  61. { myScene(id: 1) { name description role { id user { email } model } } }
  62. ORM IN GOLANG GOORM VS XORM
  63. type Scene struct {   ID int64 `xorm:"pk autoincr" json:"id"`   Image string `json:"image,omitempty"`   CreatedAt time.Time `json:"createdAt,omitempty"`   UpdatedAt time.Time `json:"updatedAt,omitempty"`   DeletedAt time.Time `xorm:"deleted"`   // reference   Items []*SceneItem `xorm:"-" json:"items,omitempty"`   User *User `xorm:"-" json:"user,omitempty"`   Role *SceneAccess `xorm:"-" json:"role,omitempty"` } Data Model user role permission
  64.  "user": &graphql.Field{    Type: userType,    Resolve: func(p graphql.ResolveParams) (interface{}, error) {      o, ok := p.Source.(*model.Shorten)      if !ok {        return nil, errMissingSource      }      if o.User != nil {        return o.User, nil      }      return getUserFromLoader(p.Context, o.UserID)    },  }, exist in model? fetch from loader
  65. GET DATA FROM DATABASE IF NOT EXIST func userBatch(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {   var results []*dataloader.Result   id, _ := helper.GetCacheID(keys[0].String())   user, err := model.GetUserByID(id.(int64))   results = append(results, &dataloader.Result{     Data: user,     Error: err,   })   return results } fetch from DB
  66. GRAPHQLDATALOADER ONLY SUPPORT MEMORY, LRU OR TTL CACHE
  67. INTEGRATE WITH REDIS SERVICE GRAPHQLDATALOADER
  68. func batchFunc(_ context.Context, keys dataloader.Keys) []*dataloader.Result { results := make([]*dataloader.Result, len(keys)) // get what you can from redis values, _ := redisClient.MGet(...keys.Keys()).Result() // make a list of everything that was not found in redis var cacheMisses map[int]string for i := range keys { if values[i] == redis.Nil { cacheMisses[i] = keys[i].String() } else { results[i] = &dataloader.Result{values[i], nil} } } // get the missing items from more expensive location (like DB) for idx, key := range cacheMisses { value, err := db.GetValues(key) // Pseudo code! redisClient.Set(key, value) results[idx] = &dataloader.Result{value, err} } return results } miss from redis fetch from DB
  69. HOW TO TEST GRAPHQL SCHEMA? GRAPHQL TESTING
  70. https://code.likeagirl.io/the-7-steps-to-a-complete-code-review-abdfd39e75f1
  71. RAILS-LIKE TEST FIXTURES FOR GO GO TEST FIXTURES https://github.com/go-testfixtures/testfixtures
  72. - id: 1 email: foo@gmail.com full_name: foo avatar: http://foo.com avatar_email: foo@gmail.com - id: 2 email: bar@gmail.com full_name: bar avatar: http://bar.com avatar_email: bar@gmail.com real data in DB
  73.     test := T{       Query: ` query QueryShortenURL ( $slug: String! ) { QueryShortenURL(slug: $slug) { url } }    `,       Schema: Schema,       Expected: &graphql.Result{         Data: map[string]interface{}{           "QueryShortenURL": map[string]interface{}{             "url": "http://example.com",           },         },       },     } graphql query expect data
  74.   params := graphql.Params{     Schema: test.Schema,     RequestString: test.Query,     Context: ctx,     VariableValues: map[string]interface{}{       "slug": "abcdef",     },   }   testGraphql(test, params, t) ctx := newContextWithUser(context.TODO(), user) user data in context graphql variable
  75. all pass testing in sqlite
  76. GRAPHQLDEPLOYMENT HOW TO DEPLOY GOLANG APP?
  77. GO-GGZ/GGZ AN URL SHORTENER SERVICE WRITTEN IN GO https://github.com/go-ggz/ggz
  78. Thanks
Publicidad