Build Your First REST API with Go
Golang Sri Lanka 2020
@imJenal
Hi! I'm Jyotsna
Agenda
- APIs
- Intro to REST
- Creating Endpoints
- HTTP Methods
- Securing API
- Writing Tests
- Best Practises
Image Source: www.calhoun.io/
@imJenal
Application Programming Interface
API
@imJenal
@imJenal
@imJenal
- REpresentational State Transfer
- Streamlined and lightweight web service
- Core Principles: Performance, scalability, simplicity, portability
- REST is an architectural style, or design pattern, for APIs.
REST, RESTful
@imJenal
REST
CLIENT
REST
SERVER
@imJenal
Why REST in Go?
@imJenal
Image Source: www.uihere.com
Let's Go
Image Source: www.deviantart.com/
@imJenal
Basic Example
@imJenal
package main
import (
"net/http"
)
func main() {
http.ListenAndServe(":3000", nil)
}
main.go
Gorilla Mux
@imJenal
- HTTP request multiplexer
- A powerful URL router and dispatcher
- matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL
import "github.com/gorilla/mux"
Router
@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
HTTP Methods
@imJenal
C R U D
Create
Read
Update
Delete
GET
PUT
POST
DELETE
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
Securing Endpoints
JWT
@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)))
}
CORS
@imJenal
Writing Tests
@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, "" total should not be nil")
assert.Equal(t, 4, total, "Expecting `4`")
}
main_test.go
Test endpoints
@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")
}
Endpoint with GET
@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")
}
Endpoint with POST
@imJenal
Best Practices
@imJenal
Abstract vs Concrete APIs
For the REST APIs,
the concrete is better than abstract
/entities
- /owners
- /blogs
- /blogposts
@imJenal
Bad URL
-
GET /getMovies – gets all the movies
-
GET /getMovieById/12 – gets the movies with the id 12
-
POST /addMovies – adds a new movie and returns the details
-
DELETE /deleteMovies/12 – removes the movies with the id 12
- GET /getMoviesByActorId/3/– gets all the movies of the actor with id 3
@imJenal
Good URL
-
GET /movies – gets all the movies
-
GET /movies/12 – gets the movies with the id 12
-
POST /movies – adds a new movie and returns the details
-
DELETE /movies/12 – removes the movies with the id 12
- GET /actor/3/movies – gets all the movies of the actor with id 3
@imJenal
Error Handling
- Request: GET https://api.twitter.com/1.1/account/settings.json
- Response: Status Code 400
{"errors":[{"code":215,"message":""Bad Authentication data"}]}
@imJenal
Status Codes
-
200 OK : Everything is working
-
201 OK : New resource has been created
-
400 Bad Request: Request can't be served
-
404 Not Found : No resource behind the URI
- 500 Internal Server Error
@imJenal
REST API Versioning
https://api.example.com/v1/authors/2/movies/13
@imJenal
https://api.example.com/v2/authors/2/movies/13
Documentation
@imJenal
@ imJenal
@ imJenal
https://slides.com/jenal/golangsl2020
Build Your First REST API
By Jyotsna Gupta
Build Your First REST API
Golang Sri Lanka | June 30, 2020
- 1,189