Develop REST API with Basic API Authentication using Go
Tags: go, tutorial, restapi, showdev
Published: 2020-08-14 08:18:53

## Table of content
- [Introduction](#introduction)
- [Objective](#objective)
- [Source Repo](#source-repo)
- [Function main](#function-main)
  - [Adding API Versioning and Basic authentication](#adding-api-versioning-and-basic-authentication)
  - [Passing Query String Parameters](#passing-query-string-parameters)
  - [REST API Listening on Port](#rest-api-listening-on-port)
- [Get Method](#get-method)
- [Full Source Code](#full-source-code)
- [Go REST API Unit testing](#go-rest-api-unit-testing)
- [Executing Go Unit Testing](#executing-go-unit-testing)
- [Go Source Build and Run](#go-source-build-and-run)
- [Real time test](#real-time-test)
  - [Curl method](#curl-method)
  - [Ngrok method](#ngrok-method)


# Introduction
We have learnt how to create simple REST API in the [previous blog](https://dev.to/chefgs/develop-rest-api-using-go-and-test-using-various-methods-8e0).
We will now see the below topics in this blog,
1. How to add REST API versioning
2. How to create query string parameters
3. How to add basic authentication to REST API
4. How to write Go unit testing for API authentication code
5. How to 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/Id456`

## Source Repo
Checkout the full source repo here in [GitHub](https://github.com/chefgs/golang/tree/master/src/apiauth)

Now let us start break the blocks and create the code!

## 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",
  }))
```

## Passing Query String Parameters
```
  subRouterAuthenticated.GET("/:IdValue", GetMethod)
```
## REST API Listening on Port
```
  listenPort := "1357"
  // Listen and Server on the LocalHost:Port
  router.Run(":"+listenPort)
```

## 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))
}
```

## 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)
}
```

## 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)
}
```

## 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
```
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
```
- 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
```

- Then proceed to test the REST API real-time

## Real time test 
### CURL method
Run `curl` with basic authentication user-password
```
curl -u username:password -i -X GET http://localhost:1357/api/v1/PersonId/Id456
```
- This will show the output as below in Console
```
"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](https://dl.equinox.io/ngrok/ngrok/stable) 
- 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         
```
- 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](https://github.com/chefgs/repo_images/blob/master/authprompt.jpg?raw=true)

- After entering the credential, the browser should show,
`GetMethod Called With Param: Id456`
![Image of Output](https://github.com/chefgs/repo_images/blob/master/output.jpg?raw=true)

## Key Take away
We have seen the below major 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