RESTful API Development with Go
Code Camp NYC 2019
@imJenal
Image Source: www.calhoun.io/
@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/codecampnyc-go