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")
}
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
package api
import (
"net/http"
"runtime/debug"
)
Expose build metadata for debugging deploys
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
export interface RetryOptions {
retries: number;
baseMs: number;
maxMs: number;
signal?: AbortSignal;
onRetry?: (attempt: number, delay: number, err: unknown) => void;
Exponential backoff with jitter for retries
module Api
module V1
class UsersController < BaseController
def show
user = User.includes(:profile).find(params[:id])
ETags for conditional requests and caching
package dbutil
import (
"context"
"github.com/jackc/pgconn"
Retry Postgres serialization failures with bounded attempts
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: String!
GraphQL API with Spring Boot
Share this code
Here's the card — post it anywhere.