go 25 lines · 1 tab

Strict JSON decode helper (size limit + unknown fields)

Leah Thompson Jan 2026
1 tab
package api

import (
  "encoding/json"
  "errors"
  "io"
  "net/http"
)

func DecodeJSON(w http.ResponseWriter, r *http.Request, dst any, maxBytes int64) error {
  r.Body = http.MaxBytesReader(w, r.Body, maxBytes)
  dec := json.NewDecoder(r.Body)
  dec.DisallowUnknownFields()

  if err := dec.Decode(dst); err != nil {
    return err
  }
  if err := dec.Decode(&struct{}{}); err != nil {
    if errors.Is(err, io.EOF) {
      return nil
    }
    return err
  }
  return errors.New("body must contain a single JSON value")
}
1 file · go Explain with highlit

Most handler bugs I debug are really input bugs: oversized bodies, unexpected fields, or clients sending arrays when the API expects an object. A dedicated decode helper makes behavior consistent. This pattern wraps the request body with http.MaxBytesReader to cap payload size, then uses a json.Decoder with DisallowUnknownFields() so typos fail fast instead of silently dropping data. I also decode a second time into an empty struct to ensure there’s no trailing junk (multiple JSON values in one request). In production, this yields better error reporting and fewer mysterious partial updates. It also prevents resource abuse: if someone sends a 50MB JSON document to your login endpoint, you’ll reject it cheaply. Pair this with clear client-facing errors (and request IDs) and your API becomes much easier to operate.


Related snips

Share this code

Here's the card — post it anywhere.

Strict JSON decode helper (size limit + unknown fields) — share card
Link copied