RESTful API Development with Go
@imJenal
Image Source: www.calhoun.io/
@imJenal
@imJenal
@imJenal
@imJenal
@imJenal
@imJenal
@imJenal
@imJenal
@imJenal
Application Programming Interface
@imJenal
@imJenal
@imJenal
@imJenal
Image Source: www.uihere.com
Image Source: www.deviantart.com/
@imJenal
@imJenal
package main
import (
    "net/http"
)
func main() {
   http.ListenAndServe(":3000", nil)
}main.go
@imJenal
import "github.com/gorilla/mux"@imJenal
package main
import (
    "encoding/json"
    "log"
    "net/http"
    "github.com/gorilla/mux"
)
func main() {
    router := mux.NewRouter()
    router.HandleFunc("/", HomeHandler)
    router.HandleFunc("/movie/{id}", MovieHandler)
    log.Fatal(http.ListenAndServe(":3000", router))
}
main.go
@imJenal
func AllMoviesEndPoint(w http.ResponseWriter, r *http.Request) {}
func FindMovieEndpoint(w http.ResponseWriter, r *http.Request) {}
func CreateMovieEndPoint(w http.ResponseWriter, r *http.Request) {}
func UpdateMovieEndPoint(w http.ResponseWriter, r *http.Request) {}
func DeleteMovieEndPoint(w http.ResponseWriter, r *http.Request) {}
func main() {
	router := mux.NewRouter()
	router.HandleFunc("/movies", AllMoviesEndPoint).Methods("GET")
	router.HandleFunc("/movies", CreateMovieEndPoint).Methods("POST")
	router.HandleFunc("/movies", UpdateMovieEndPoint).Methods("PUT")
	router.HandleFunc("/movies", DeleteMovieEndPoint).Methods("DELETE")
	router.HandleFunc("/movies/{id}", FindMovieEndpoint).Methods("GET")
	log.Fatal(http.ListenAndServe(":3000",router))
}@imJenal
@imJenal
package dao
import (
	"log"
	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)
type MoviesDAO struct {
	Server   string
	Database string
}
var db *mgo.Database
const (COLLECTION = "movies")
func (m *MoviesDAO) Connect() {
	session, err := mgo.Dial(m.Server)
	if err != nil {
		log.Fatal(err)
	}
	db = session.DB(m.Database)
}@imJenal
dao.go
type Movie struct {
	ID          string        `bson:"_id"`
	Name        string        `json:"name"`
	CoverImage  string        `json:"cover_image"`
	Description string        `json:"description"`
}@imJenal
model.go
Basic model : Movie
func (m *MoviesDAO) FindAll() ([]Movie, error) {
	var movies []Movie
	err := db.C(COLLECTION).Find(bson.M{}).All(&movies)
	return movies, err
}
func (m *MoviesDAO) FindById(id string) (Movie, error) {
	var movie Movie
	err := db.C(COLLECTION).FindId(bson.ObjectIdHex(id)).One(&movie)
	return movie, err
}
func (m *MoviesDAO) Insert(movie Movie) error {
	err := db.C(COLLECTION).Insert(&movie)
	return err
}
func (m *MoviesDAO) Delete(movie Movie) error {
	err := db.C(COLLECTION).Remove(&movie)
	return err
}
func (m *MoviesDAO) Update(movie Movie) error {
	err := db.C(COLLECTION).UpdateId(movie.ID, &movie)
	return err
}@imJenal
func FindMovieEndpoint(w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	movie, err := dao.FindById(params["id"])
	if err != nil {
		respondWithError(w, http.StatusBadRequest, "Invalid Movie ID")
		return
	}
	respondWithJson(w, http.StatusOK, movie)
}@imJenal
@imJenal
@imJenal
func main() {
    router := mux.NewRouter()
    allowedHeaders := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"})
    
    allowedMethods := handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS"})
    
    allowedOrigins := handlers.AllowedOrigins([]string{*})
    
    log.Fatal(http.ListenAndServe(":3000", handlers.CORS(allowedHeaders, allowedMethods, allowedOrigins)(router)))
}@imJenal
@imJenal
package main
func Add(value1 int, value2 int) int {
    return value1 + value2
}
func main() { }main.go
@imJenal
package main
import (
    "testing"
    "github.com/stretchr/testify/assert"
)
func TestAdd(t *testing.T) {
    total := Add(1, 3)
    assert.NotNil(t, total, "The `total` should not be `nil`")
    assert.Equal(t, 4, total, "Expecting `4`")
}
main_test.go
@imJenal
func TestEndpointGET(t *testing.T) {
    request, _ := http.NewRequest("GET", "/create", nil)
    response := httptest.NewRecorder()
    router := mux.NewRouter()
    router.Handle("/create", EndpointGET).Methods("GET")
    router.ServeHTTP(response, request)
    assert.Equal(t, 200, response.Code, "OK response is expected")
}@imJenal
func TestEndpointPOST(t *testing.T) {
    person := &Person{
        Firstname: "Nic",
        Lastname: "Raboy"
    }
    jsonPerson, _ := json.Marshal(person)
    request, _ := http.NewRequest("POST", "/create", bytes.NewBuffer(jsonPerson))
    response := httptest.NewRecorder()
    router := mux.NewRouter()
    router.Handle("/create", EndpointPOST).Methods("POST")
    router.ServeHTTP(response, request)
    assert.Equal(t, 200, response.Code, "OK response is expected")
}@imJenal
@imJenal
For the REST APIs,
the concrete is better than abstract
/entities
{"errors":[{"code":215,"message":""Bad Authentication data"}]}https://api.example.com/v1/authors/2/movies/13
@imJenal
Slides: https://slides.com/jenal/golang-jenal2019