You're reading a sample of this book. Get the full version here.
Let's Go Further Sending JSON Responses › Fixed-Format JSON
Previous · Contents · Next
Chapter 3.1.

Fixed-Format JSON

Let’s begin by updating our healthcheckHandler to send a well-formed JSON response which looks like this:

{"status": "available", "environment": "development", "version": "1.0.0"}

At this stage, the thing I’d like to emphasize is that JSON is just text. Sure, it has certain control characters that give the text structure and meaning, but fundamentally, it is just text.

So that means you can write a JSON response from your Go handlers in the same way that you would write any other text response: using w.Write(), io.WriteString() or one of the fmt.Fprint functions.

In fact, the only special thing we need to do is set a Content-Type: application/json header on the response, so that the client knows it’s receiving JSON and can interpret it accordingly.

Let’s do exactly that.

Open up the cmd/api/healthcheck.go file and update the healthcheckHandler as follows:

File: cmd/api/healthcheck.go
package main

import (
    "fmt"
    "net/http"
)

func (app *application) healthcheckHandler(w http.ResponseWriter, r *http.Request) {
    // Create a fixed-format JSON response from a string. Notice how we're using a raw
    // string literal (enclosed with backticks) so that we can include double-quote 
    // characters in the JSON without needing to escape them? We also use the %q verb to 
    // wrap the interpolated values in double-quotes.
    js := `{"status": "available", "environment": %q, "version": %q}`
    js = fmt.Sprintf(js, app.config.env, version)

    // Set the "Content-Type: application/json" header on the response. If you forget to
    // this, Go will default to sending a "Content-Type: text/plain; charset=utf-8"
    // header instead.
    w.Header().Set("Content-Type", "application/json")

    // Write the JSON as the HTTP response body.
    w.Write([]byte(js))
}

Once you’ve made those changes, restart the API, open a second terminal, and use curl to make a request to the GET /v1/healthcheck endpoint. You should now get back a response which looks like this:

$ curl -i localhost:4000/v1/healthcheck
HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 06 Apr 2021 08:38:12 GMT
Content-Length: 73

{"status": "available", "environment": "development", "version": "1.0.0"}

You might also like to try visiting localhost:4000/v1/healthcheck in your browser.

If you’re running one of the newer versions of Firefox, you’ll find that it knows the response contains JSON (due to the Content-Type header) and it will present the response using the inbuilt JSON viewer. Like so:

03.01-01.png

If you click on the Raw Data tab you should see the original unformatted JSON response:

03.01-02.png

Or you can even select the Pretty Print option to add whitespace, like so:

03.01-03.png

Of course, using a fixed-format string like we are in this chapter is a pretty simple and lo-fi approach to generating a JSON response. But it’s worth remembering that it is a valid option. It can be useful for API endpoints that always return the same static JSON, or as a quick and easy way to generate small dynamic responses like we have here.


Additional Information

JSON charset

In your programming career you might have come across other JSON APIs which send responses with the header Content-Type: application/json; charset=utf-8.

Including a charset parameter like this isn’t normally necessary. The JSON RFC states:

JSON text exchanged between systems that are not part of a closed ecosystem MUST be encoded using UTF-8.

The important word here is “must”. Because our API will be a public-facing application, it means that our JSON responses must always be UTF-8 encoded. And it also means that it’s safe for the client to assume that the responses it gets are always UTF-8 encoded. Because of this, including a charset=utf-8 parameter is redundant.

The RFC also explicitly notes that the application/json media type does not have a charset parameter defined, which means that it is technically incorrect to include one too.

Basically, in our application a charset parameter serves no purpose, and it’s safe (and also correct) to not include one in our Content-Type: application/json header.