
Continue from the previous topic…
There is one behaviour in our Golang web application that will frustrate our users. Everytime we add, update, or delete a video record, the web page gets refreshed. So if at that time there is a video being played, then poof, it’s gone after you add, update, or delete a record. That’s a bad UX.
To overcome there, I decide to implement RESTful web services in the Golang project and the frontend will use VueJS library to update the web page.
Firstly, we need to wrap a web service interface over the CRUD functions we have in our web application. JSON will be used as the data transport format. To do that, we will introduce a new handler function to multiplex request to the correct function in our RESTful web service.
func handleVideoAPIRequests(video models.IVideo) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
var err error
switch request.Method {
case "GET":
err = handleVideoAPIGet(writer, request, video)
case "POST":
err = handleVideoAPIPost(writer, request, video)
case "PUT":
err = handleVideoAPIPut(writer, request, video)
case "DELETE":
err = handleVideoAPIDelete(writer, request, video)
}
if err != nil {
util.CheckError(err)
return
}
}
}
HTTP GET
Then for each of the HTTP methods, we will process the request independently. For example, to retrieve the list of videos or one of the videos, we will use GET method, i.e. handleVideoAPIGet().

func handleVideoAPIGet(writer http.ResponseWriter, request *http.Request, video models.IVideo) (err error) {
videoIDURL := path.Base(request.URL.Path)
var output []byte
if videoIDURL == "video" {
videos, errIf := video.GetAllVideos()
err = errIf
util.CheckError(errIf)
output, errIf = json.MarshalIndent(&videos, "", "\t")
err = errIf
util.CheckError(errIf)
writer.Header().Set("Content-Type", "application/json")
writer.Write(output)
return
}
videoID, err := strconv.Atoi(videoIDURL)
if err != nil {
util.CheckError(err)
return
}
err = video.GetVideo(videoID)
util.CheckError(err)
output, err = json.MarshalIndent(&video, "", "\t")
util.CheckError(err)
writer.Header().Set("Content-Type", "application/json")
writer.Write(output)
return
}
This method seems long but in the first half of the function, it is checking if the URL ends with /video or it ends with a number. If it ends with /video, that means it is not asking for a specific video but it’s asking for all valid videos. Thus video.GetAllVideos() is called and the videos are returned as JSON array.
So what if it’s only requesting for particular video with video ID? There is where the second half of the function comes in. It will first convert the part of the URL into an integer using strconv.Atoi(). Then we will retrieve the video based on that integer as video ID and then return it as a JSON object.
HTTP POST
To create a new video record in database, the system will call the handleVideoAPIPost().
func handleVideoAPIPost(writer http.ResponseWriter, request *http.Request, video models.IVideo) (err error) {
length := request.ContentLength
body := make([]byte, length)
request.Body.Read(body)
json.Unmarshal(body, &video)
err = video.CreateVideo()
if err != nil {
util.CheckError(err)
apiStatus := models.APIStatus{
Status: false,
Message: err.Error(),
}
output, err := json.MarshalIndent(&apiStatus, "", "\t")
util.CheckError(err)
writer.WriteHeader(400)
writer.Header().Set("Content-Type", "application/json")
writer.Write(output)
} else {
apiStatus := models.APIStatus{
Status: true,
Message: "A video is successfully added to the database.",
}
output, err := json.MarshalIndent(&apiStatus, "", "\t")
util.CheckError(err)
writer.WriteHeader(200)
writer.Header().Set("Content-Type", "application/json")
writer.Write(output)
}
return
}
The beginning of the function for POST method is reading from body is because we will post a JSON object containing the new video record information to it. So it needs to retrieve the JSON object from the body.
Another interesting thing in this function is that it will return JSON object indicating whether the action of adding new record is successful or not with a corresponding HTTP status code of 400 or 200. Doing so is to let the frontend feedback to the user so that the user knows whether the record is successfully inserted to the database or not.
HTTP PUT
How about if we want to update existing video record? Well, we can rely on the PUT method. The PUT method requires us to tell it which resource it will update. Hence the function handleVideoAPIPut() is as follows.
func handleVideoAPIPut(writer http.ResponseWriter, request *http.Request, video models.IVideo) (err error) {
videoIDURL := path.Base(request.URL.Path)
videoID, err := strconv.Atoi(videoIDURL)
if err != nil {
util.CheckError(err)
return
}
err = video.GetVideo(videoID)
if err != nil {
util.CheckError(err)
apiStatus := models.APIStatus{
Status: false,
Message: err.Error(),
}
output, err := json.MarshalIndent(&apiStatus, "", "\t")
util.CheckError(err)
writer.WriteHeader(400)
writer.Header().Set("Content-Type", "application/json")
writer.Write(output)
}
length := request.ContentLength
body := make([]byte, length)
request.Body.Read(body)
json.Unmarshal(body, &video)
err = video.UpdateVideo()
if err != nil {
util.CheckError(err)
apiStatus := models.APIStatus{
Status: false,
Message: err.Error(),
}
output, err := json.MarshalIndent(&apiStatus, "", "\t")
util.CheckError(err)
writer.WriteHeader(400)
writer.Header().Set("Content-Type", "application/json")
writer.Write(output)
} else {
apiStatus := models.APIStatus{
Status: true,
Message: "A video record is successfully updated.",
}
output, err := json.MarshalIndent(&apiStatus, "", "\t")
util.CheckError(err)
writer.WriteHeader(200)
writer.Header().Set("Content-Type", "application/json")
writer.Write(output)
}
return
}
Similar to how we have done in handleVideoAPIGet(), we first need to get the video ID from the URL with the help of strconv.Atoi(). Then we will check whether there is an existing video in the database with the video ID. If there is none, then we simply return JSON object updating frontend with an error message. If there is video found with the video ID, we will then proceed to update it with the info from the JSON object passed via the request body.
There is one thing to take note here is that we are not replacing the existing video record entirely. We are only updating part of it. So the JSON object should contain only the fields needed to be updated.

HTTP DELETE
The function to handle DELETE method will be similar to the one handling PUT method.
func handleVideoAPIDelete(writer http.ResponseWriter, request *http.Request, video models.IVideo) (err error) {
videoIDURL := path.Base(request.URL.Path)
videoID, err := strconv.Atoi(videoIDURL)
if err != nil {
util.CheckError(err)
return
}
err = video.GetVideo(videoID)
if err != nil {
util.CheckError(err)
apiStatus := models.APIStatus{
Status: false,
Message: err.Error(),
}
output, err := json.MarshalIndent(&apiStatus, "", "\t")
util.CheckError(err)
writer.WriteHeader(400)
writer.Header().Set("Content-Type", "application/json")
writer.Write(output)
}
err = video.DeleteVideo()
if err != nil {
util.CheckError(err)
apiStatus := models.APIStatus{
Status: false,
Message: err.Error(),
}
output, err := json.MarshalIndent(&apiStatus, "", "\t")
util.CheckError(err)
writer.WriteHeader(400)
writer.Header().Set("Content-Type", "application/json")
writer.Write(output)
} else {
apiStatus := models.APIStatus{
Status: true,
Message: "A video record is deleted.",
}
output, err := json.MarshalIndent(&apiStatus, "", "\t")
util.CheckError(err)
writer.WriteHeader(200)
writer.Header().Set("Content-Type", "application/json")
writer.Write(output)
}
return
}
Similar to how we have done in handleVideoAPIPut(), we first need to get the video ID from the URL with the help of strconv.Atoi(). Then we will check whether there is an existing video in the database with the video ID. If there is none, then we simply return JSON object updating frontend with an error message. If there is video found with the video ID, we will then proceed to delete it.

Frontend with VueJS
Now, let’s see how we use VueJS library to display the video list.

It is done with just a for loop to list down all the relevant videos and having values stored in data attributes for update and delete the video record.
References
- Why is jQuery AJAX Sending a GET Instead of a POST?
- [Stack Overflow] PostgreSQL: Character with Byte Sequence 0xc2 0x81 in Encoding “UTF8” has no Equivalent in Encoding “WIN1252”;
- Go: Convert int64 to string;
- [Stack Overflow] What is the Best Way to Convert byte Array to string?
- [Stack Overflow] How to Use img src in VueJS?
- [Vue.js Guide] What is VueJS?
- [Vue.js Guide] List Rendering;
- [Vue.js Guide] Event Handling;
- Common Mistakes to Avoid while Working with VueJS;
- [Stack Overflow] How to Send a PUT/DELETE request in jQuery?

2 thoughts on “RESTful Web Service in Golang and Front-end Served with VueJS”