Files

Kata 16: The HTTP Client Hygiene Wrapper

Target Idioms: net/http Transport Reuse, Timeouts, Context-First APIs, Response Body Draining
Difficulty: 🔴 Advanced

🧠 The "Why"

“Works locally” HTTP code in Go often fails in prod because people:

  • use http.DefaultClient with no timeouts,
  • create a new client/transport per request (connection churn),
  • forget to close bodies (leaks + no keep-alive reuse),
  • dont drain bodies (prevents connection reuse).

This kata is about building a small internal SDK the Go way.

🎯 The Scenario

Your service calls a downstream API that sometimes returns large error bodies and sometimes hangs. You need:

  • strict timeouts,
  • proper cancellation,
  • safe connection reuse,
  • structured logs.

🛠 The Challenge

Implement:

  • type APIClient struct { ... }
  • func (c *APIClient) GetJSON(ctx context.Context, url string, out any) error

1. Functional Requirements

  • Use http.NewRequestWithContext.
  • Decode JSON on 2xx responses into out.
  • On non-2xx: read up to N bytes of body and return an error including status code.

2. The "Idiomatic" Constraints (Pass/Fail Criteria)

  • Must NOT use http.DefaultClient.
  • Must configure timeouts (Client.Timeout and/or transport-level timeouts).
  • Must reuse a single Transport (connection pooling).
  • Must defer resp.Body.Close().
  • Must drain (at least partially) error bodies to allow connection reuse.
  • Use slog with fields: method, url, status, latency.

🧪 Self-Correction (Test Yourself)

  • If connections spike under load: you probably rebuild transports.
  • If keep-alives dont work: you likely didnt drain/close body.
  • If hangs occur: you likely lack correct timeout configuration.

📚 Resources