Make POST request in Go using net/http

Author

Gurleen Sethi on 17 December 2022

Make POST request in Go using net/http

HTTP Post request #

Make an HTTP Post request with a json request body.

package main

import (
	"bytes"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"net/url"
)

const BASE_URL = "jsonplaceholder.typicode.com"

type PostBody struct {
	Title string `json:"title"`
}

func main() {
	// Prepare URL
	postURL := url.URL{
		Host:   BASE_URL,
		Path:   "/todos",
		Scheme: "https",
	}

	// Prepare request body
	body := PostBody{
		Title: "Hello World",
	}

	bodyBytes, err := json.Marshal(&body)
	if err != nil {
		log.Fatal(err)
	}

	reader := bytes.NewReader(bodyBytes)

	// Make HTTP POST request
	resp, err := http.Post(postURL.String(), "application/json", reader)
	if err != nil {
		log.Fatal(err)
	}

	// Close response body
	defer func() {
		err := resp.Body.Close()
		if err != nil {
			log.Fatal(err)
		}
	}()

	// Read response body
	responseBody, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	if resp.StatusCode >= 400 && resp.StatusCode <= 500 {
		log.Println("Error response. Status Code: ", resp.StatusCode)
	}

	log.Println("Response:", string(responseBody))
}
main.go

Custom HTTP Client #

The above uses the deafult http client πŸ‘‰ http.Post. http.Post calls the http.DefaultClient, here is the behind the scenes code.

func Post(url, contentType string, body io.Reader) (resp *Response, err error) {
	return DefaultClient.Post(url, contentType, body)
}

You can create custom http client πŸ‘‰ httpClient := http.Client{}.

// Make HTTP POST request
httpClient := http.Client{}

resp, err := httpClient.Post(postURL.String(), "application/json", reader)
if err != nil {
	log.Fatal(err)
}

Using http.Do #

Here is another way to make the request using the Do function.

request, err := http.NewRequest(http.MethodPost, postURL.String(), reader)
if err != nil {
	log.Fatal(err)
}

request.Header.Set("Content-Type", "application/json")

httpClient := &http.Client{}

resp, err := httpClient.Do(request)
if err != nil {
	log.Fatal(err)
}

Reusable function #

As you can notice making a post request can be verbose, it involves a bunch of projects. What I do in some of my projects is create a reusable function to do some of the repeated stuff.

It would look something like this,

func JSONPost(url url.URL, body any) (string, error) {
	bodyBytes, err := json.Marshal(&body)
	if err != nil {
		return "", nil
	}

	reader := bytes.NewReader(bodyBytes)

	// Make HTTP POST request
	request, err := http.NewRequest(http.MethodPost, url.String(), reader)
	if err != nil {
		return "", nil
	}

	request.Header.Set("Content-Type", "application/json")

	httpClient := &http.Client{}

	resp, err := httpClient.Do(request)
	if err != nil {
		return "", nil
	}

	// Close response body
	defer func() {
		err := resp.Body.Close()
		if err != nil {
			log.Fatal(err)
		}
	}()

	// Read response body
	responseBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", nil
	}

	if resp.StatusCode >= 400 && resp.StatusCode <= 500 {
		return string(responseBody), errors.New("400/500 status code error")
	}

	return string(responseBody), nil
}

And then use it,

func main() {
	// Prepare URL
	postURL := url.URL{
		Host:   BASE_URL,
		Path:   "/todos",
		Scheme: "https",
	}

	// Prepare request body
	body := PostBody{
		Title: "Hello World",
	}

	responseBody, err := JSONPost(postURL, &body)
	if err != nil {
		log.Fatal(err)
	}

	log.Println("Response:", string(responseBody))
}

The signaure of reusable JSONPost can be changed according to need, for example I usually also parse the response body into a struct as well instead of returning a string response.

Table of Contents
Get updates via email πŸ‘ˆ

Get notified once/twice per month when new articles are published.

TheDeveloperCafe
Copyright Β© 2022 TheDeveloperCafe.
The Go gopher was designed by Renee French.