Handling HTTP request in Go Echo framework (1)

In the last post, we have discussed about how we could setup nested templates in Echo framework.

In the following sections, we will move on and see how we could make HTTP request in Echo server and render the result back to the client. Let’s start with the example we did in the last article.

Setup an API endpoint

Firstly, create a new API endpoint and return a simple JSON which is later called by the Echo server itself.

Create new endpoint in the api folder

Unlike a hardcoded JSON request, our new API endpoint takes the request input from JSON data, form data or query parameters as defined in the model/example_request.go and then return it the the client.

model/example_request.go

1
2
3
4
5
6
package model

type ExampleRequest struct {
  FirstName string `json:"first_name" form:"first_name" query:"first_name"`
  LastName  string `json:"last_name" form:"last_name" query:"last_name"`
}

api/get_full_name.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package api

import (
  "fmt"
  "net/http"

  "github.com/labstack/echo"

  "gitlab.com/ykyuen/golang-echo-template-example/model"
)

func GetFullName(c echo.Context) error {
  // Bind the input data to ExampleRequest
  exampleRequest := new(model.ExampleRequest)
  if err := c.Bind(exampleRequest); err != nil {
    return err
  }

  // Manipulate the input data
  greeting := exampleRequest.FirstName + " " + exampleRequest.LastName

  return c.JSONBlob(
    http.StatusOK,
    []byte(
      fmt.Sprintf(`{
        "first_name": %q,
        "last_name": %q,
        "msg": "Hello %s"
      }`, exampleRequest.FirstName, exampleRequest.LastName, greeting),
    ),
  )
}

Setup the route for the above API endpoint

Add our new API endpoint route in main.go.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
func main() {
  // Echo instance
  e := echo.New()

  // Instantiate a template registry with an array of template set
  // Ref: https://gist.github.com/rand99/808e6e9702c00ce64803d94abff65678
  templates := make(map[string]*template.Template)
  templates["home.html"] = template.Must(template.ParseFiles("view/home.html", "view/base.html"))
  templates["about.html"] = template.Must(template.ParseFiles("view/about.html", "view/base.html"))
  e.Renderer = &TemplateRegistry{
    templates: templates,
  }

  // Route => handler
  e.GET("/", handler.HomeHandler)
  e.GET("/about", handler.AboutHandler)

  // Route => api
  e.GET("/api/get-full-name", api.GetFullName)

  // Start the Echo server
  e.Logger.Fatal(e.Start(":1323"))
}

Test the new API endpoint

Run the Echo server as see if it works.

It works!

It works!

This new endpoint could serve the browser client directly or it could be called by the Echo server itself.

Call the API from Echo server

Create the response struct type

Similar to the model/example_request.go, we need to define another struct type for the HTTP response.

model/example_response.go

1
2
3
4
5
6
7
package model

type ExampleResponse struct {
  FirstName string `json:"first_name"`
  LastName  string `json:"last_name"`
  Msg       string `json:"msg"`
}

Show the new greeting message in the about page

Let’s modify the about page handler such that it will show the greeting message.

handler/about_handler.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package handler

import (
  "encoding/json"
  "io/ioutil"
  "net/http"

  "github.com/labstack/echo"

  "gitlab.com/ykyuen/golang-echo-template-example/model"
)

func AboutHandler(c echo.Context) error {

  tr := &http.Transport{}
  client := &http.Client{Transport: tr}

  // Call the api
  resp, err := client.Get(
    "http://localhost:1323/api/get-full-name?first_name=Kit&last_name=Yuen",
  )

  if err != nil {
    return err
  }

  defer resp.Body.Close()
  body, err := ioutil.ReadAll(resp.Body)

  // Unmarshal the response into a ExampleResponse struct
  var exampleResponse model.ExampleResponse
  if err = json.Unmarshal(body, &exampleResponse); err != nil {
    return err
  }

  // Please note the the second parameter "about.html" is the template name and should
  // be equal to one of the keys in the TemplateRegistry array defined in main.go
  return c.Render(http.StatusOK, "about.html", map[string]interface{}{
    "name": "About",
    "msg": exampleResponse.Msg,
  })
}

Test the about page

Start the Echo server and browse the about page. We could see the new greeting message.

The greeting msg is now composed of the result of the API call.

The greeting msg is now composed of the result of the API call.

The project structure

After making the above changes, the project structure should look like this. (The project is renamed to handling-http-request-in-go-echo-example-1, make sure you set the import statement in the Golang source files correctly if you want to change the project name.)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
handling-http-request-in-go-echo-example-1/
  ├── api/                     # folder of api endpoints
  │   ├── get_full_name.go     # api for get full name
  ├── handler/                 # folder of request handlers
  │   ├── home_handler.go      # handler for home page
  │   └── about_handler.go     # handler for about page
  ├── model/                   # folder of custom struct types
  │   ├── example_request.go   # struct type of get_full_name request
  │   └── example_response.go  # hstruct type of get_full_name response
  ├── vendor/                  # dependencies managed by dep
  │   ├── github.com/*
  │   └── golang.org/*
  ├── view/                    # folder of html templates
  │   ├── base.html            # base layout template
  │   ├── home.html            # home page template
  │   └── about.html           # about page template
  ├── Gopkg.lock               # dep config file
  ├── Gopkg.toml               # dep config file
  └── main.go                  # programme entrypoint

Summary

This article shows you how to setup a simple API endpoint in Echo framework. Since Go is a strongly typed language, we have to convert the JSON into well defined struct type before for we could manipulate its content. For example:

  • Binding the GET request input to exampleRequest in api/get_full_name.go.
  • Unmarshal the GET response into exampleResponse in handler/about_handler.go.

To invoke the API call, we could simply make use of the Go standard net/http library.

The complete example could be found on gitlab.com.