Develop REST API using Go with API versioning, Basic Auth and Query String

Saravanan Gnanaguru - May 4 '22 - - Dev Community

Table of content


Introduction

We have learnt how to create simple REST API in the previous blog.
We will see the below topics in this blog,
How to,

  • Add REST API versioning
  • Create query string parameters
  • Add basic authentication to REST API
  • Write Go unit testing for API authentication code
  • Test the REST API with authentication in real time

Objective

  • We will be creating REST API that listens on localhost port 1357 and has the API versioning with query string parameter.
  • Sample URL format we are planning to create, http://localhost:1357/api/v1/PersonId/Id432

Source Repo

Checkout the full source repo here in GitHub

Let us start break the code into blocks,

Function main

Add the below code function main()

Adding API Versioning and Basic authentication

  // Define gin router
  router := gin.Default()

  // Create Sub Router for customized API version and basic auth
  subRouterAuthenticated := router.Group("/api/v1/PersonId", gin.BasicAuth(gin.Accounts{
    "basic_auth_user": "userpass",
  }))
Enter fullscreen mode Exit fullscreen mode

Passing Query String Parameters

  subRouterAuthenticated.GET("/:IdValue", GetMethod)
Enter fullscreen mode Exit fullscreen mode

REST API Listening on Port

  listenPort := "1357"
  // Listen and Server on the LocalHost:Port
  router.Run(":"+listenPort)
Enter fullscreen mode Exit fullscreen mode

Get Method

  • Define a GetMethod function and add the following code
  • It fetches and prints the Person IdValue from the query string parameter passed in the API URL
func GetMethod(c *gin.Context) {
  fmt.Println("\n'GetMethod' called")
  IdValue := c.Params.ByName("IdValue")
  message := "GetMethod Called With Param: " + IdValue
  c.JSON(http.StatusOK, message)

  // Print the Request Payload in console
  ReqPayload := make([]byte, 1024)
  ReqPayload, err := c.GetRawData()
  if err != nil {
        fmt.Println(err)
        return
  }
  fmt.Println("Request Payload Data: ", string(ReqPayload))
}
Enter fullscreen mode Exit fullscreen mode

Full Source Code

// Golang REST API with API versioning and Basic Auth
package main

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

func GetMethod(c *gin.Context) {
  fmt.Println("\n'GetMethod' called")
  IdValue := c.Params.ByName("IdValue")
  message := "GetMethod Called With Param: " + IdValue
  c.JSON(http.StatusOK, message)

  ReqPayload := make([]byte, 1024)
  ReqPayload, err := c.GetRawData()
  if err != nil {
        fmt.Println(err)
        return
  }
  fmt.Println("Request Payload Data: ", string(ReqPayload))
}

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

  // Create Sub Router for  customised API version
  subRouterAuthenticated := router.Group("/api/v1/PersonId", gin.BasicAuth(gin.Accounts{
    "admin": "adminpass",
  }))

  subRouterAuthenticated.GET("/:IdValue", GetMethod)

  listenPort := "1357"
  // Listen and Server on the LocalHost:Port
  router.Run(":"+listenPort)
}
Enter fullscreen mode Exit fullscreen mode

Go REST API Unit testing

  • Go testing module can be used for creating unit testing code for Go source

  • Go Unit testing code looks as below,

// Golang REST API unit testing program
package main

import (
  "testing"
  "net/http"
  "net/http/httptest"
  "fmt"
  "bytes"

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

    // 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!
    body := bytes.NewBuffer([]byte("{\"ApiTest\":\"GetReq\"}"))
    req, err := http.NewRequest("GET", "/api/v1/PersonId/Id456", body)
    if err != nil {
        t.Fatalf("Couldn't create request: %v\n", err)
    }
    req.Header.Set("Content-Type", "application/json")
    req.SetBasicAuth("admin", "adminpass")

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

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

    // Perform the request
    router.ServeHTTP(w, req)
    fmt.Println(w.Body)
}
Enter fullscreen mode Exit fullscreen mode

Executing Go Unit Testing

Run the command go test and it shows the below output in console,

'GetMethod' called
Request Payload Data:  {"ApiTest":"GetReq"}
[GIN] 2020/08/03 - 12:26:29 | 200 |         7.2µs |                 | GET      "/api/v1/PersonId/Id456"
"GetMethod Called With Param: Id456"
PASS
ok      apiauth 0.008s
Enter fullscreen mode Exit fullscreen mode

This confirms the REST API code we have created is working fine.

Go Source Build and Run

  • If we want to test the API in our server, on which the code is created, run the below command
go run apiauth.go
Enter fullscreen mode Exit fullscreen mode
  • This command invokes the REST API on localhost port 1357 & console shows the below output,
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /api/v1/PersonId/:IdValue --> main.GetMethod (4 handlers)
[GIN-debug] Listening and serving HTTP on :1357
Enter fullscreen mode Exit fullscreen mode
  • Then proceed to test the REST API real-time

Real time test

Localhost method

Run curl with basic authentication user-password

curl -u admin:adminpass -i -X GET http://localhost:1357/api/v1/PersonId/Id456
Enter fullscreen mode Exit fullscreen mode
  • This will show the output as below in Console
"GetMethod Called With Param: Id456"
Enter fullscreen mode Exit fullscreen mode
  • In Ubuntu, Access the localhost URL in browser and enter the basic auth username and password when prompted
  • After entering the credential, the browser should show, GetMethod Called With Param: Id456

Ngrok method

  • Execute the go run command to start the API, go run apiauth.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 1357(Port defined in go API code) as below,

./ngrok http 1357 and prints the output as follows in console

ngrok by @inconshreveable                                                    (Ctrl+C to quit)

Session Status                online                                                         
Session Expires               7 hours, 12 minutes                                            
Version                       2.3.35                                                         
Region                        United States (us)                                             
Web Interface                 http://127.0.0.1:4040                                          
Forwarding                    http://ca6d2c4cee3e.ngrok.io -> http://localhost:4000          
Forwarding                    https://ca6d2c4cee3e.ngrok.io -> http://localhost:4000         
Enter fullscreen mode Exit fullscreen mode
  • ngrok generates a dynamic URL. For ex: http://ca6d2c4cee3e.ngrok.io

  • The REST API can be tested by adding the URL in browser address bar,
    http://ca6d2c4cee3e.ngrok.io/api/v1/PersonId/Id456

  • Browser will prompt to enter the authentication details. So enter credentials
    Image of Authprompt

  • After entering the credential, the browser should show,
    GetMethod Called With Param: Id456
    Image of Output

Conclusion

We have seen the below topics in this blog,

  • Adding basic authentication to REST API
  • Go unit testing for API authentication code
  • Testing the REST API with basic authentication in real time

  • 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.

Follow me on,

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .