Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building a webapp with go - a post mortem repor...

GoDays
January 23, 2020

Building a webapp with go - a post mortem report - Martin Radile - Aleri Solutions

This talk is a post mortem report about building a mobile first web application with go as the backend language and Vue.js as the frontend language. The focus is on the backend parts written in go.
The talk will begin with a brief overview of the problem that we solved.
The main part will then give an opinionated view of the libraries used for the most important backend parts:
Building a REST API: https://github.com/labstack/echo
We chose the echo framework because it had a good set of features helping us to speed up the development.

GoDays

January 23, 2020
Tweet

More Decks by GoDays

Other Decks in Technology

Transcript

  1. Whoami? • Martin Radile • History in Content Management •

    Backend Developer & DevOps • Developer Relations Go @ Aleri Solutions @m_radile 2
  2. What is This Talk About? • A post mortem report

    about developing a web app in go • Which libraries where useful (and why)? • Which libraries where not useful (and why)? • Some code snippets 3
  3. Task Task • Car accident reporting tool • Team of

    five (UX, PO, PM, Frontend & Backend Developer • Budget: ~45 Days Technical Requirements • Free Choice of Technology • SAML2 Login (Central IDP) • Sending Reports by Email (+Images) • Mobile first • Unkown Hosting Plattform 4
  4. Summary Frontend Single Page Application (Vue.js) Backend written in Go

    with • github.com/labstack/echo • github.com/jinzhu/gorm • github.com/russellhaering/gosaml2 • github.com/urfave/cli • github.com/GeertJohan/go.rice • github.com/sirupsen/logrus • github.com/nfnt/resize • github.com/stretchr/testify • github.com/golang/mock Finished • on time • in budget • at the promised level of quality End of Talk ;-) 5
  5. Echo Echo is a web framework • Features • Routing

    • Request binding to structs • Embedding & serving static files • Middlewares • and more… 7
  6. Echo - Basics func main() { e := echo.New() e.GET("/ping",

    func(c echo.Context) error { return c.String(http.StatusOK, "pong") }) log.Fatal(e.Start(":5000")) } 8
  7. Echo - Routing func main() { e := echo.New() e.POST("/projects",

    Add) e.GET("/projects/:id", Get) e.PUT("/projects/:id", Update) e.DELETE("/projects/:id", Delete) e.GET("/projects", All) log.Fatal(e.Start(":5000")) } 9
  8. Echo - Request & Response type Project struct { ID

    string `json:"id"` Name string `json:"name"` } func Add(c echo.Context) error { var p Project if err := c.Bind(&p); err != nil { return err } //do something with p return c.JSON(http.StatusOK, &p) } 10
  9. Echo • Easy to learn • Echo „wasn’t in the

    way“ • Excellent documentation • Lots of examples • Fun to use 11
  10. Gorm ORM in go • Most popular ORM in go

    • Relation modelling • Support for multiples DBs • Hook support (CRUD Events) • Simple Migrations • No SQL needed • DB handle accessible 12
  11. Gorm - Modelling type Person struct { ID uint `gorm:"primary_key"`

    Name string } type Role struct { ID uint `gorm:"primary_key"` Name string PersonID int Person Person } 13
  12. Gorm - Modelling type Person struct { ID uint `gorm:"primary_key"`

    Name string } type Role struct { ID uint `gorm:"primary_key"` Name string PersonID int Person Person } 14 func main() { db, _ := gorm.Open("postgres", conStr) defer db.Close() db.AutoMigrate(&Person{}) db.AutoMigrate(&Role{}) r := Role{ Name: "a role", Person: Person{ Name: "somebody", }, } if err := db.Create(&r).Error; err != nil { //handle error } }
  13. Gorm • Good for quick prototyping • Reduces boilerplate code

    • Documentation could be better • Support for multiple DBs did not work (e.g. time.Time) • On balance potentially more trouble than benefit 15
  14. gosaml2 SAML2 library • Only SAML2 implementation for go •

    Pure Go implementation • Request signing & encrypting 16
  15. Logrus Structured logging library • Multiple log formats • (colored)

    Text • JSON • + more plug-ins • Default fields • Drop in replacement for standard logging 18
  16. Logrus - Basics package main import log "github.com/sirupsen/logrus" func main()

    { log.Print(„hello godays") } ==================================================================================== INFO[0000] hello godays 19
  17. Logrus - Setup func main() { log.SetFormatter(&log.JSONFormatter{}) log.SetLevel(log.InfoLevel) log.Info(„hello godays")

    } ========================================================================================== {"level":"info","msg":"hello godays","time":"2020-01-22T19:03:39+01:00"} 20
  18. Logrus - Fields func main() { log.WithFields(log.Fields{ "project_id": 42, }).Info("added

    entry") } ==================================================================================== INFO[0000] added entry project_id=42 21
  19. Logrus - Default Fields func main() { logger := log.WithFields(log.Fields{

    "layer": "database", }) logger.Info(„connected successful") } ==================================================================================== INFO[0000] connection successful layer=database 22
  20. Logrus - Default Fields func main() { logger := log.WithFields(log.Fields{

    "layer": "database", }) logger.Info("connection succesful") logger.WithFields(log.Fields{ "project_id": 42, }).Info("added entry") } =================================================================================== INFO[0000] connection successful layer=database INFO[0000] connection successful layer=database project_id=42 23
  21. Conclusion • Echo did help a lot • Gorm was

    good start, but backfired later • Gosaml2 was ok, but would choose OAuth2 • Logrus was very useful • Go was a perfect fit 25
  22. Gorm - Basics package main import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/postgres"

    ) func main() { con := "host=localhost port=5432 user=go dbname=go password=go sslmode=true“ db, err := gorm.Open("postgres", con) if err != nil { panic("could not connect to database") } defer db.Close() } 27