Slide 1

Slide 1 text

Building REST API with GoLang Burak AYDIN @4pps

Slide 2

Slide 2 text

#dfist Why Go ● Rapid development ● Production ready ● Community ● Performance

Slide 3

Slide 3 text

#dfist Performance ● “Rewriting the EventMachine push backend to Go we went from 250k connections per node to 1.5 million connections per node” ● “Full integration test suite dropped from 25 minutes to 2 minutes.” ● “The time to do a full API server deploy with rolling restarts dropped from 30 minutes to 3 minutes” http://blog.parse.com/learn/how-we-moved-our-api-from-ruby-to-go-and-saved-our-sanity/

Slide 4

Slide 4 text

#dfist Go Lang ● Static Typed ● Interfaces, types, structs ● Garbage Collection ● Concurrency The gopher is covered under Creative Commons Attribution 3.0 license. They were designed by Renée French, who also designed Glenda, the Plan 9 bunny. Additional info can be found here.

Slide 5

Slide 5 text

#dfist Basic Middleware func main() { http.HandleFunc("/hello", handleHello) log.Fatal(http.ListenAndServe("localhost:8080", nil)) } func handleHello(w http.ResponseWriter, req *http.Request) { fmt.Fprintln(w, "Hello, 世界!") } main.go

Slide 6

Slide 6 text

#dfist Gorilla Mux - github.com/gorilla/mux Routing func main() { r := mux.NewRouter() v1 := r.PathPrefix("/api/v1").Subrouter() v1.Methods("GET").Path("/shops").Handler(route.ListShopsHandler) http.ListenAndServe(":8080", v1) } main.go

Slide 7

Slide 7 text

#dfist http.Handler func ListShopsHandler(w http.ResponseWriter, r *http.Request) { q := ctx.M(r).DB("").C("shops").Find(nil) shops := []model.Shop{} err := q.All(&shops) // error handling w.WriteHeader(http.StatusOK) err = json.NewEncoder(w).Encode(shops) if err != nil { log.Println(err) } } shop.go

Slide 8

Slide 8 text

#dfist http.Handler type Handler interface { ServeHTTP(ResponseWriter, *Request) } // The HandlerFunc type is an adapter to allow the use of // ordinary functions as HTTP handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a // Handler object that calls f. type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }

Slide 9

Slide 9 text

#dfist http.Handler signature func(w http.ResponseWriter, r *http.Request) Avoid using global variables, share values over context ● Adapter Pattern ● Chain Packages ● Complex handlers ● Handler Closures

Slide 10

Slide 10 text

#dfist Adapter Pattern type Adapter func(http.Handler) http.Handler func Logging(l *log.Logger) Adapter { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { l.Println(r.Method, r.URL.Path) h.ServeHTTP(w, r) }) } } adapter.go

Slide 11

Slide 11 text

#dfist Adapter Pattern func Adapt(h http.Handler, adapters ...Adapter) http.Handler { for _, adapter := range adapters { h = adapter(h) } return h } func main() { l := log.New(os.Stdout, "server:", log.Lshortfile) db, _ := mgo.Dial("localhost") defer db.Close() http.Handle("/", Adapt(http.HandlerFunc(index), Mongo(db), Logging(l), Cors)) http.ListenAndServe("localhost:8080", nil) } func index(rw http.ResponseWriter, req *http.Request) { fmt.Fprint(rw, "Hello world") } adapter.go

Slide 12

Slide 12 text

#dfist Adapter Pattern with context func Mongo(dbSession *mgo.Session) Adapter { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { dbcopy := dbSession.Copy() defer dbcopy.Close() context.Set(r, "db", dbcopy) h.ServeHTTP(w, r) }) } } adapter.go Gorilla Context - github.com/gorilla/context

Slide 13

Slide 13 text

#dfist Adapter Pattern with context func handleThingsRead(w http.ResponseWriter, r *http.Request) { db := context.Get(r, "db").(*mgo.Session) s, _ := db.DatabaseNames() fmt.Fprint(w, s) } adapter.go

Slide 14

Slide 14 text

#dfist Chain Packages ● github.com/codegangsta/negroni ● github.com/justinas/alice ● github.com/alexedwards/stack

Slide 15

Slide 15 text

#dfist Chain Sample var ( appChain = chain.New( mware.Mongo, mware.Cors, Logger) ) func handler() http.Handler { r := mux.NewRouter() v1 := r.PathPrefix("/api/v1").Subrouter() v1.Methods("GET").Path("/shops").Handler(appChain.ThenFunc(route.ListShopsHandler)) return v1 } routes.go

Slide 16

Slide 16 text

#dfist Complex Handlers Don’t break http.handler interface! func handleThingsRead(w http.ResponseWriter, r *http.Request, db *mgo.Session) { s, _ := db.DatabaseNames() fmt.Fprint(w, s) } main.go

Slide 17

Slide 17 text

#dfist Http Closures gist.github.com/tsenart/5fc18c659814c078378d func main() { db, err := sql.Open("postgres", "…") if err != nil { log.Fatal(err) } logger := log.New(os.Stdout, "", 0) http.Handle("/hello", withMetrics(logger, helloHandler(db))) http.ListenAndServe(":8080", nil) } main.go

Slide 18

Slide 18 text

#dfist JSON in Go func Marshal(v interface{}) ([]byte, error) func Unmarshal(data []byte, v interface{}) error func (dec *Decoder) Decode(v interface{}) error func (enc *Encoder) Encode(v interface{}) error encoding/json

Slide 19

Slide 19 text

#dfist JSON in Go Interface maps raw_data := []byte(`{"msg": "Hello Go!", "id": 12345}`) decoded := map[string]interface{}{} err := json.Unmarshal(raw_data, &decoded) if err != nil { log.Fatal(err) } fmt.Println(decoded["msg"].(string), int(decoded["id"].(float64)))

Slide 20

Slide 20 text

#dfist JSON in Go Exported struct with tag type ExportedMessageWithTags struct { Msg string `json:"msg"` Id int64 `json:"id"` } func WithTags() { raw_data := []byte(`{"msg": "Hello Go!", "id": 12345}`) decoded := ExportedMessageWithTags{} err := json.Unmarshal(raw_data, &decoded) if err != nil { log.Fatal(err) } fmt.Println(decoded.Msg, decoded.Id) }

Slide 21

Slide 21 text

#dfist JSON in Go Try to use Interface and json.RawMessage combination for complex json const input = ` { "type": "sound", "msg": { "description": "dynamite", "authority": "the Bruce Dickinson" } }` type Envelope struct { Type string Msg interface{} } type Sound struct { Description string Authority string }

Slide 22

Slide 22 text

#dfist JSON in Go var msg json.RawMessage env := Envelope{ Msg: &msg, } if err := json.Unmarshal([]byte(input), &env); err != nil { log.Fatal(err) } switch env.Type { case "sound": var s Sound if err := json.Unmarshal(msg, &s); err != nil { log.Fatal(err) } var desc string = s.Description fmt.Println(desc) default: log.Fatalf("unknown message type: %q", env.Type) }

Slide 23

Slide 23 text

#dfist Respond github.com/unrolled/render func main() { r := render.New(render.Options{}) mux := http.NewServeMux() // This will set the Content-Type header to "application/json; charset=UTF-8". mux.HandleFunc("/json", func(w http.ResponseWriter, req *http.Request) { r.JSON(w, http.StatusOK, map[string]string{"hello": "json"}) }) http.ListenAndServe("127.0.0.1:3000", mux) }

Slide 24

Slide 24 text

#dfist Web Frameworks ● Negroni ● Gin ● Beego ● Revel github.com/avelino/awesome-go#web-frameworks

Slide 25

Slide 25 text

#dfist Other Subjects ● Go routine ● Channels ● Database ● Auth ● Error ● JWT ● Microservices ● Test

Slide 26

Slide 26 text

Thank you! Questions? Questions? @burakaydn

Slide 27

Slide 27 text

#dfist Resources ● https://go-talks.appspot.com/github.com/matryer/golanguk/building-apis.slide ● https://go-talks.appspot.com/github. com/stephandollberg/talks/go_meetup_zurich_fall_2015/meetup.slide ● https://github.com/ilgooz/stack ● http://eagain.net/articles/go-dynamic-json/ ● https://medium.com/@benbjohnson/structuring-applications-in-go- 3b04be4ff091#.30276vcjc