Writing Json Responses
Fast recall
Quick revision
Core answer
Whether you understand Writing Json Responses beyond memorized syntax.
Giving a definition without explaining a practical trade-off.
Connect the concept to a small production example and state its complexity or failure mode.
Interview-ready guide
Interview answer
The pattern, executed:
func usersHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"name": "Al",
"role": "dev",
})
}
// GET /users → {"name":"Al","role":"dev"} with Content-Type: application/json — verifiedThe two steps in order: header, then encode. The header MUST be set before Encode writes (the body send flushes the headers — setting Content-Type after is a no-op).
With a status code (non-200):
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated) // 201
json.NewEncoder(w).Encode(user)The order matters: Header() → WriteHeader() → Encode(). Once WriteHeader or the first write happens, headers are committed.
The error-response convention every API needs:
func writeError(w http.ResponseWriter, status int, msg string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(map[string]string{"error": msg})
}The alternative: json.Marshal → w.Write(b) — the marshal-to-then-write form. Encoder is preferred (single step, streaming — writes as it marshals, no intermediate []byte allocation). The only reason to Marshal first: modifying the bytes (encryption, custom wrapping) or testing the marshal error separately.
Writing Json Responses
Direct Answer
Set the Content-Type header, then json.NewEncoder(w).Encode(v) — it marshals AND writes to the ResponseWriter in one step. Set the status first if non-200 (w.WriteHeader(200) before Encode).
Deep Dive & Architecture
The Encoder's mechanics: json.NewEncoder(w).Encode(v) marshals v incrementally and writes directly to w — no intermediate buffer for the full JSON. The trade-off: you can't know the byte count ahead (no Content-Length from the encoder) — Go handles this with chunked transfer encoding for HTTP/1.1 (or streams for HTTP/2). For small responses, the difference is negligible; for large ones, the streaming avoids a full-copy.
The Encode error path, which people miss: Encode returns an error — but AFTER potentially writing partial bytes to w. The common handler ignores it (w is already committed — what would you do?). The honest pattern: for the critical first-write, Marshal first, check the error, then Write — for streaming large payloads, accept the partial-write-on-error semantics.
The API-envelope convention, worth naming as the architectural choice: many teams wrap all responses in {"data": ..., "error": ...} or the JSON:API format. The writeJSON helper encapsulates the decision:
func writeJSON(w http.ResponseWriter, status int, v any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(v)
}
One function, consistent ordering, every handler uses it — the bug (header after write) becomes structurally impossible.
Common Mistakes & Pitfalls
Giving a definition without explaining a practical trade-off.
Check your understanding
Answer these without looking back at the article.
- When would you use Writing Json Responses in a real project?
- How would you compare Writing Json Responses with an alternative?
- What is a common mistake when using Writing Json Responses?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.