Desenvolvendo Web APIs

em Go

Bianca Rosa

  • Python / Go / Lua / JS
  • PythOnRio / Gophers RJ
  • Pyladies / Rails Girls RJ / WWG RJ
  • Sobre frameworks
  • Escrita de código: padrões e clean code
  • Estrutura de pastas
  • Testes
  • Gerenciando dependências
  • Dockerfile
  • Erros comuns
  • Quando usar Go

Devo ou não usar um framework web em Go?

A standard library do Go é bastante poderosa.

Portanto, não utilizar um framework é perfeitamente aceitável.

#leitura: Why I Don’t Use Go Web Frameworks

#disclaimer: eu gosto de utilizar gin / echo.

package main

import (
	"fmt"
	"io"
	"net/http"
)

func hello(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Hello, TDC!")
}

func main() {
	http.HandleFunc("/", hello)
	fmt.Println("Serving http...")
	http.ListenAndServe(":8000", nil)
}
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

type Greeting struct {
    Message string `json:"msg"`
}

func hello(w http.ResponseWriter, r *http.Request) {
    person := Greeting{Message: "Hello"}
    jsonPerson, err := json.Marshal(person)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(jsonPerson)
}

func main() {
    http.HandleFunc("/", hello)
    fmt.Println("Serving http...")
    http.ListenAndServe(":8000", nil)
}

github.com/biancarosa/golang-http-server

Frameworks

  • Echo
  • Gin
  • Martini
  • Beego
  • Revel
  • Traffic
  • ... e outros que eu não conheço

Ou ainda...

gorilla

      1. Você é uma pessoa livre.
      2. Apenas baseie sua decisão em bons argumentos.
      3. Muito do que está escrito na Internet são opiniões.

github.com/biancarosa/goasync

package main

import (
    "os"

    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
    log "github.com/sirupsen/logrus"

    "github.com/biancarosa/goasync/routes"
)

func main() {
    e := echo.New()

    // Setup Logrus
    log.SetOutput(os.Stdout)
    log.SetLevel(log.DebugLevel)

    // Middleware
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    // Routes
    e.GET("/", routes.HealthCheck)
    e.POST("/async", routes.ExecuteTask)
    e.GET("/tasks/:uuid", routes.RetrieveTask)

    e.Logger.Fatal(e.Start(":1323"))
}
package routes

import (
    "net/http"
    "os"

    "github.com/labstack/echo"
    log "github.com/sirupsen/logrus"
)

func init() {
    // Setup Logrus
    log.SetOutput(os.Stdout)
    log.SetLevel(log.DebugLevel)
}

//HealthCheck is the route that prints a sucessful message when the application is fine.
func HealthCheck(c echo.Context) error {
    return c.String(http.StatusOK, "I seem to be perfectly fine.")
}

Escreva bons códigos

#leitura: Good Code vs Bad Code in Golang

#slides: Boas Práticas & Clean Code com Golang

Effective Go
Go wiki

Use Interfaces

Estrutura de pastas

* totalmente minha opinião

  • routes
  • services
  • models
  • configuration

Testes de Unidade

Se o código é dificil de testar, então provavelmente merece ser reescrito.

"SÓ QUERIA UM MONKEY PATCH IGUAL AO DO PYTHON"

Rosa, Bianca, 3am, sábado

github.com/bouk/monkey

Testes de Integração

Separação de testes unitários e de integração através de build tags.

// +build integration

Testes de Carga

github.com/tsenart/vegeta

Dependências

github.com/golang/dep

Dockerfile

FROM golang:latest AS build

WORKDIR $GOPATH/{your-repo}
COPY . ./
RUN go get github.com/tools/godep
RUN godep get
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix nocgo -o /app .

FROM scratch
COPY  --from=build /app ./
ENTRYPOINT ["./app"]

gist

Erros comuns

//Task is the structure that contains all relevant information for a task
type Task struct {
    Method string
    URL    string
    name   string
    UUID   uuid.UUID
}

{ 
    "Method" : "GET",
    "URL" : "http://google.com",
    "UUID": "3b4f73b3-ee18-46b5-b9ed-b1e26573794f"
}

Campos privados de uma struct não são serializados nem deserializados!

//Task is the structure that contains all relevant information for a task
type Task struct {
    Method string
    URL    string
    Name   string
    UUID   uuid.UUID
}

{ 
    "Method" : "GET",
    "Name": "Get Google",
    "URL" : "http://google.com",
    "UUID": "3b4f73b3-ee18-46b5-b9ed-b1e26573794f"
}

Não tente reinventar a roda

Ex: logrus

Nunca ignore erros!

session, e_ := mgo.Dial("db:27017")
defer session.Close()
session, err := mgo.Dial("db:27017")
if err != nil {
    return
}
defer session.Close()

Quando usar Go

  • Performance
  • Go Routines
  • Confiabilidade

twitter: @__biancarosa

slides: biancarosa.com.br/slides/tdc-go-web-apis.html

tks :)