Pick a depth. Each prompt opens in your AI pre-loaded with the lesson. Click a row to preview the prompt.
Most real APIs in 2026 are JSON-over-HTTP. You accept a JSON body, parse it, validate it, do your work, and return a JSON response. The failure mode you'll see most often is: someone sent you malformed JSON, or JSON with the wrong shape, and your handler crashed. Defining your request shape (a struct or schema) and validating every incoming request is the single most valuable habit to form on day one.
Accepting a JSON body means three distinct steps: parsing the bytes as JSON, validating the shape (the right fields, the right types), and acting on the result. Every framework handles each differently — some validate at the type level, some require explicit middleware. Returning a consistent envelope ({ ok: true, data: ... } vs { ok: false, error: ... }) makes every caller's job easier and makes your API debuggable at a glance.
curl -X POST http://localhost:8080/users -H 'content-type: application/json' -d '{"name":"Ada","email":"a@b.com"}'. See 201.-d '{not json'. See 400.-d '{}'. See 422.-d '{"name":"Ada","email":"nope"}'. In Python (pydantic) and Node (zod) this is 422. In Go it's 201 (manual validation didn't check format). Note the difference.package main
import (
"encoding/json"
"net/http"
)
type CreateUser struct {
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
http.HandleFunc("POST /users", func(w http.ResponseWriter, r *http.Request) {
var body CreateUser
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, "invalid JSON: "+err.Error(), 400)
return
}
if body.Name == "" || body.Email == "" {
http.Error(w, "name and email are required", 422)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(201)
json.NewEncoder(w).Encode(map[string]any{
"ok": true,
"user": body,
})
})
http.ListenAndServe(":8080", nil)
}go run main.go