Table of Contents
Introduction
- In my previous blog, we have developed
Hello World!
program in Go. - We will see, how to develop REST API using Golang in this article.
Objective
- We will create a simple REST API, which runs on the
localhost
port4000
- 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,
- API Endpoint URL
- HTTP Request Method (or Verb)
- 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
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 theREST API controller framework
for our code.
Setup Golang development environment
- Refer the DevTo Article Here, to setup Golang dev environment.
Start development
- Create directory to develop Golang api
cd $GOHOME/src
mkdir api
- Create file
api.go
inapi
directory Every go code starts with package name definition
package main
Then we need to declare the module import section
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 "/"
router.POST("/", PostMethod)
router.GET("/", GetMethod)
- Listen to the specified port of localhost
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
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
// 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
Unit Testing of Go REST API
- Please refer to the Unit testing code added
api_test.go
- It uses the Go "testing" module
- In order to test our
GetMethod
andPostMethod
functions, we need to add the mock POST and GET request implemented in the code, by creating test functionsTestGetMethod
andTestPostMethod
- Here is the full source of
api_test.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
- 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 theapi
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
Deploy using Google Cloud Kubernetes Cluster
Create API service using Google Cloud Kubernetes Engine
- Login into Google Cloud Console
- Open Navigation menu from the left and choose
Compute > Kubernetes Engine
- Choose
Clusters > Create Cluster
and follow instructions to create Cluster - Next, choose
Workloads > Deploy
to create the container workload and attach it to the Cluster created above - It is possible to create the container by linking the "github" repository
- Choose the appropriate
Dockerfile
to create image, in this repo, Dockerfile located under the pathsrc/api/
- The image created as part of this deployment will be stored into
Google Container Registry (GCR)
- Once the kube deployment is successful, we then have to expose the Service as
API Endpoint
to consume the REST API functions. - Choose
expose
option and provide the port number, in which the REST API application has been configured in the code. - In our case, we have exposed the API at
port 4000
- After exposing the Kube workload, it generates the
publicIP:4000
for us to utilise the API.
Note: Refer the Google documentation for connecting the Github repo with GCP.
Code Structure
./api
|__api.go
|__api_test.go
|__go.mod
|__go.sum
|__Dockerfile
|__README.md
Key Takeaways
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
Bibliography
REST API
Go Gin REST API framework
Thanks for reading!
If you like the blog, please show some support and re-share.
Top comments (4)
Very Amazing :) Thanks for sharing Saravanan ...
Thanks Dinesh.. it's part of my Go tutorial series.. more to come🙂
Cool ;)
best golang test tutorial thank u so much
Some comments may only be visible to logged-in visitors. Sign in to view all comments.