How to Develop REST API using Go and Test using various methods
Tags: go, api, tutorial, beginners
Published: 2022-04-26 16:35:02

## Table of Contents
- [Introduction](#introduction)
- [Objective](#objective)
- [What is REST API?](#what-is-rest-api)
- [What makes a RESTful API?](#what-makes-a-restful-api)
  - [API Endpoint URL](#api-endpoint-url)
  - [HTTP Request Method](#http-request-method)
  - [The Response](#the-response)
- [Develop REST API using Go](#develop-rest-api-using-go)
  - [Why to choose Go for REST API?](#why-to-choose-go-for-rest-api)
  - [Go modules for REST API creation](#go-modules-for-rest-api-creation)
  - [Setup Golang development environment](#setup-golang-development-environment)
  - [Start development](#start-development)
    - [In function main](#in-function-main)
    - [POST and GET methods](#post-and-get-methods)
    - [Full Go source for REST API](#full-go-source-for-rest-api)
    - [Build the code](#build-the-code)
    - [Writing Go Test Program](#writing-go-test-program)
    - [Go Test Program Structure](#go-test-program-structure)
    - [Go Test Program to Test API](#go-test-program-to-test-api)
- [REST API RealTime Testing](#rest-api-realtime-testing)
  - [Ngrok method](#ngrok-method)
  - [Test via Curl](#test-via-curl)
  - [Deploy API using docker container](#deploy-api-using-docker-container)
- [Conclusion](#conclusion)
- [Further Reference](#further-reference)

## **Introduction**
- In this blog we will see, the basics of REST API and how to develop REST API using Go.
- We will also discuss testing the REST API using various methods

## Objective
- We will create a simple REST API, which runs on the `localhost` port `4000`
- We will also see, how to test the REST API endpoint in Real Time using various methods.

## **What is REST API?**
REST is acronym for REpresentational State Transfer. It is architectural style for distributed hypermedia systems and was first presented by Roy Fielding in 2000 in his famous dissertation.

Like any other architectural style, REST also does have it’s own 6 guiding constraints which must be satisfied if an interface needs to be referred as RESTful. These principles are listed below.

REST is not a standard but a set of recommendations and constraints for RESTful web services. 

These include:

- **Client-Server**: SystemA makes an HTTP request to a URL hosted by SystemB, which returns a response. 
It’s identical to how a browser works. The application makes a request for a specific URL. The request is routed to a web server that returns an HTML page. That page may contain references to images, style sheets, and JavaScript, which incur further requests and responses.

- **Stateless**: REST is stateless: the client request should contain all the information necessary to respond to a request. 
In other words, it should be possible to make two or more HTTP requests in any order and the same responses will be received.

- **Cacheable**: A response should be defined as cacheable or not.

- **Layered**: The requesting client need not know whether it’s communicating with the actual server, a proxy, or any other intermediary.

- **Uniform interface** – REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state.

- **Code on demand** (optional) – REST allows client functionality to be extended by downloading and executing code in the form of applets or scripts. This simplifies clients by reducing the number of features required to be pre-implemented.

## **What makes a RESTful API?**
A REST API consists of,
1. API Endpoint URL
2. HTTP Request Method (or Verb)
3. The Response returned from API request

### API Endpoint URL
An application implementing a RESTful API will define one or more URL endpoints with a domain, port, path, and/or querystring — for example, `https://somedomain:port/book/bookname?format=json`
Where, 
`https://somedomain` - Domain
`port` - Port number on which the API exposed
`/book` - Route Path
`bookname?format=json` - Query String

### HTTP Request Method
The above defined API endpoint URL will be serving HTTP methods, which can be perform create, read, update and delete functions defined within the application.

- GET	- read -	returns requested data
- POST - create -	creates a new record
- PUT - update - updates an existing record
- DELETE - delete -	deletes an existing record

### The Response
The response payload can be whatever is practical: data, HTML, an image, an audio file, and so on. Data responses are typically JSON-encoded, but XML, CSV, simple strings, or any other format can be used. You could allow the return format to be specified in the request either in JSON or XML format.

Each response header returns either of the HTTP status codes,
```
200 OK
201 Created
400 Bad Request
404 Not Found
401 Unauthorized
500 Internal server error
```
# **Develop REST API using Go**
## Why to choose Go for REST API?
- It is fast
- It is simple to understand
- It is compiled and works well with Microservices

## Go modules for REST API creation
We will be using below modules for our development
- `net/http`
- [**`Gin-gonic`**](https://github.com/gin-gonic/gin) REST API framework. It is a Go opensource utility module, which can be readily available to use and provides extensive REST API features in Go.
It provides the `REST API controller framework` for our code.

## Setup Golang development environment
- Refer the [DevTo Article Here](https://dev.to/chefgs/getting-started-with-go-in-ubuntu-16-04-3b59), to setup Golang dev environment.

## Start development
- Create directory to develop Golang api
```
cd $GOHOME/src
mkdir api
```
- Create file `api.go` in `api` directory
- Every go code starts with package name definition
`package main`

- Then we need to declare the module import section
```go
import (
  "fmt"
  "net/http"
  "github.com/gin-gonic/gin"
)
```
### In function main
- Add gin controller to handle the HTTP events coming to API

`router := gin.Default()`

- Add functions to handle the POST and GET requests on URL route path "/"
```go
  router.POST("/", PostMethod)
  router.GET("/", GetMethod)
```

- Listen to the specified port of localhost
```go
  listenPort := "4000"
  // Listen and Server on the LocalHost:Port
  router.Run(":"+listenPort)
```

### POST and GET methods
Define the Post and Get methods to handle the requests coming to API in "/" route path

```go
func PostMethod(c *gin.Context) {
  fmt.Println("\napi.go 'PostMethod' called")
  message := "PostMethod called"
  c.JSON(http.StatusOK, message)
}

func GetMethod(c *gin.Context) {
  fmt.Println("\napi.go 'GetMethod' called")
  message := "GetMethod called"
  c.JSON(http.StatusOK, message)
}
```
### Full Go source for REST API
Please find the [source code here](https://github.com/chefgs/golang/tree/master/src/api)
```go
// Golang REST API program
package main

import (
  "fmt"
  "net/http"
  "github.com/gin-gonic/gin"
)

func PostMethod(c *gin.Context) {
  fmt.Println("\napi.go 'PostMethod' called")
  message := "PostMethod called"
  c.JSON(http.StatusOK, message)
}

func GetMethod(c *gin.Context) {
  fmt.Println("\napi.go 'GetMethod' called")
  message := "GetMethod called"
  c.JSON(http.StatusOK, message)
}

func main() {
  router := gin.Default()

  router.POST("/", PostMethod)
  router.GET("/", GetMethod)

  listenPort := "4000"
  // Listen and Server on the LocalHost:Port
  router.Run(":"+listenPort)
}
```

### Build the code
Use the below commands to compile and build Go source,
```
go mod init api
go get -d "github.com/gin-gonic/gin"
go build api.go # to create the executable in current path
or
go install api.go # to create the executable in GOBIN path
go run api.go
```

## Writing Go Test Program
- Go provides the built-in functionality to test your Go code. Hence, we don't need an expensive setup or 3rd party libraries to create unit tests.
- Go has the module called `testing` to achieve the same.
- So if we want to test a go program, we can create a unit test file with `<source_go_filename>_test.go`.
- For example: We have created `api.go` source file. So create test file as `api_test.go` to unit test the `api.go` functionality.

## Go Test Program Structure
- Go test program also follows the similar statement pattern as main source go file.
- It should have the below sections,
  - `package` statement
  - `import` statement
  - `func main()`

## Go Test Program to Test API 
- Please refer to the Unit testing code added `api_test.go`
- It uses the Go "testing" module 
- In order to test our `GetMethod` and `PostMethod` functions, we need to add the mock POST and GET request implemented in the code, by creating test functions `TestGetMethod` and `TestPostMethod`
- Here is the full source of `api_test.go`
```go
// Golang REST API unit testing program
package main

import (
  "testing"
  "net/http"
  "net/http/httptest"
  "fmt"
  "github.com/gin-gonic/gin"
)

func TestPostMethod(t *testing.T) {
    // Switch to test mode so you don't get such noisy output
    gin.SetMode(gin.TestMode)

    // Setup your router, just like you did in your main function, and
    // register your routes
    r := gin.Default()
    r.POST("/", PostMethod)

    // Create the mock request you'd like to test. Make sure the second argument
    // here is the same as one of the routes you defined in the router setup
    // block!
    req, err := http.NewRequest(http.MethodPost, "/", nil)
    if err != nil {
        t.Fatalf("Couldn't create request: %v\n", err)
    }

    // Create a response recorder so you can inspect the response
    w := httptest.NewRecorder()

    // Perform the request
    r.ServeHTTP(w, req)
    fmt.Println(w.Body)

    // Check to see if the response was what you expected
    if w.Code == http.StatusOK {
        t.Logf("Expected to get status %d is same ast %d\n", http.StatusOK, w.Code)
    } else {
        t.Fatalf("Expected to get status %d but instead got %d\n", http.StatusOK, w.Code)
    }
}

func TestGetMethod(t *testing.T) {
    // Switch to test mode so you don't get such noisy output
    gin.SetMode(gin.TestMode)

    // Setup your router, just like you did in your main function, and
    // register your routes
    r := gin.Default()
    r.GET("/", GetMethod)

    // Create the mock request you'd like to test. Make sure the second argument
    // here is the same as one of the routes you defined in the router setup
    // block!
    req, err := http.NewRequest(http.MethodGet, "/", nil)
    if err != nil {
        t.Fatalf("Couldn't create request: %v\n", err)
    }

    // Create a response recorder so you can inspect the response
    w := httptest.NewRecorder()

    // Perform the request
    r.ServeHTTP(w, req)
    fmt.Println(w.Body)

    // Check to see if the response was what you expected
    if w.Code == http.StatusOK {
        t.Logf("Expected to get status %d is same ast %d\n", http.StatusOK, w.Code)
    } else {
        t.Fatalf("Expected to get status %d but instead got %d\n", http.StatusOK, w.Code)
    }
}
```
- Below command can be used to verify the Unit Testing of REST API, 
```
go test
```


# **REST API RealTime Testing**
- We will now see, how we can test the REST API in real-time.
- I have explained 4 different ways to test the REST API,
  - Ngrok method
  - Test via `curl` command
  - Deploy as docker container
  - Deploy using Google Cloud Kube Clouster

## Ngrok method
- Execute the `go run` command to start the API,
`go run api.go`
- To verify our REST API, we need to expose the localhost of the server to internet
- So we can use "ngrok" for this purpose
- Download [ngrok here](https://dl.equinox.io/ngrok/ngrok/stable) 
- Extract the ngrok executable in some location on your server.
- Start ngrok on port 4000(Port defined in go API code) as below,

`./ngrok http 4000`
- ngrok generates a dynamic URL. For ex: `http://123er5678.ngrok.io`

- The REST API can be tested by adding the URL in browser address bar, 
`http://123er5678.ngrok.io/` 

- The browser should show,
`GetMethod Called`

## Test via Curl
- Otherwise, we can use curl command inside same server to verify the endpoint URL, return success.

- Execute the `go run` command to start the API,
`go run api.go`

- In the same server, in which our REST API is running, execute the below commands, to verify the API

- POST method
`curl -X POST http://localhost:4000/`

- The console output should show,
`PostMethod Called`

- GET method
`curl -X GET http://localhost:4000/`

- The console output should show,
`GetMethod Called`

## Deploy API using docker container
- Create a `Dockerfile` in the `api` directory. Use the file here as reference.
```
FROM golang:latest # Use alpine version if you prefer smaller size

# Set necessary environmet variables needed for our image
ENV GO111MODULE=on \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64

WORKDIR /root/go/src/api

COPY . .

RUN go get -d -v ./...
RUN go install -v ./...

# Export necessary port
EXPOSE 4000

CMD ["api"]
```
- Then Run following commands to Build and Run the `docker image`
```
docker build -t apigopgm . --rm
docker image ls
docker run -p 4000:4000 --name apicontainer --rm apigopgm
```
- Now the REST API can be accessible from the endpoint URL,
`http://localhost:4000/`

- Now using Curl or Ngrok test method, we can test the API.

- After the testing is completed, we can stop the container.
```
docker container ls
docker stop apicontainer
```


## Code Structure
```
./api
|__api.go
|__api_test.go
|__go.mod
|__go.sum
|__Dockerfile
|__README.md

```

## Conclusion
We have gone through below topics in this blogs,
- What is REST API
- How to develop simple REST API using golang
- How to unit test the REST API
- Various methods of testing the REST API in realtime

- This article was previously published on my dev community personal profile, re-publishing for the benefit of a wider audience.
- Hope this article is helpful to people getting started with kubernetes concepts.

### Further Reference
[REST API](https://restfulapi.net/)
[Go Gin REST API framework](https://github.com/gin-gonic/gin)
[Go Source code](https://github.com/chefgs/golang/tree/master/src/api)
```
Thanks for reading! 
If you like the blog, please show some support and re-share.
```

### Follow me on,
- [GitHub](https://github.com/chefgs)
- [LinkedIn](https://www.linkedin.com/in/saravanan-gnanaguru-1941a919/)
- [Twitter](https://twitter.com/saransid)
- [gsaravanan.dev](https://gsaravanan.dev)