This page looks best with JavaScript enabled

Sending POST Request in Go with a Body

 ·   ·  ☕ 3 min read

Introduction

Send a POST request in golang is pretty daunting if you have a post body and you’re coming from a scripting language like JavaScript or Python. Here in Go, schema for JSON needs to be defined beforehand in order to marshal and unmarshal string back and forth to JSON.

Simple POST

This marshaling/unmarshalling could be an oneliner if your request body is not nested, or is one level deep, or there is not even a body. Consider this example:

1
2
3
4
5
func main() {
  requestBody, err := json.Marshal(map[string]string{"key": "value"})

  res, _ := http.Post("https://httpbin.org/post", "application/json", bytes.NewBuffer(requestBody))
}

POST with a Body

But imagine a request body like this, which is a sample body I am using to send a POST request to Elasticsearch:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "sort": [ {"@timestamp": {"order": "desc"} }],
  "query": {
    "match": {
      "path.to.keyword": {
        "query": "the-term"
      }
    }
  }
}

If it was JavaScript, I could have simply sent post request like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
let endpoint = "https://httpbin.org/post"
let query = {
    "sort": [ {"@timestamp": {"order": "desc"} }],
    "query": {
        "match": {
            "path.to.keyword": {
                "query": "the-term"
            }
        }
    }
}

fetch(endpoint, {
    method: "POST",
    body: JSON.stringify(query),
    headers: {
        "Content-Type": "application/json",
    },
})

But this is not dealing with scripting language!

Create Schema, aka struct

To do this in go way, we need to do this in this manner:

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
type Request struct {
    Query Query  `json:"query"`
    Sort  []Sort `json:"sort"`
}

type Sort struct {
    Timestamp Timestamp `json:"@timestamp"`
}

type Timestamp struct {
    Order string `json:"order"`
}

type Query struct {
    Match Match `json:"match"`
}

type Match struct {
    PathToKeyword PathToKeyword `json:"path.to.keyword"`
}

type PathToKeyword struct {
    Query string `json:"query"`
}

The Driver Code

Followed by the driver code to make a POST request, which would be:

 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
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    requestBody, err := json.Marshal(Response{
        Query: Query{
            Match: Match{
                PathToKeyword: PathToKeyword{
                    Query: "the-term",
                },
            },
        },
        Sort: []Sort{
            {
                Timestamp: Timestamp{
                    Order: "desc",
                },
            },
        },
    })

  res, _ := http.Post("https://httpbin.org/post", "application/json", bytes.NewBuffer(requestBody))
  defer res.Body.Close()

  body, _ := ioutil.ReadAll(res.Body)
  fmt.Println(string(body))
}

Just for the sake of simplicity, I’ve decided not to handle errors.

Conclusion

You may find this nuance of go not very user friendly, but this was not one of the design golang of the language. And someone has well said… (in Gopher Slack)

Go is very much a non-magical language, you have to actually program :)

Peter Bourgon

Related Links:

Share on

santosh
WRITTEN BY
santosh
Pipeline & Backend Developer