Learn Go← Dashboard

fmt, io, os, bufio, encoding/json, net/http, time, slog, regexp, flag, embed.

net/http Client & Server

Go's HTTP server is in the standard library and is genuinely production-ready — it's what runs huge chunks of the internet, by way of services that just import net/http.

A tiny HTTP server

go playground
Loading...

In a real server:

log.Fatal(http.ListenAndServe(":8080", nil))
  • http.HandleFunc(pattern, fn) registers a function as a handler for pattern.
  • A handler is any value with ServeHTTP(w http.ResponseWriter, r *http.Request).
  • http.ResponseWriter is an io.Writer plus headers and status code.
  • *http.Request has the URL, method, headers, query params, and body.

A JSON endpoint

http.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{
        "name": "Ada",
    })
})

Routing in Go 1.22+

Since Go 1.22, the built-in mux understands methods and path values:

mux := http.NewServeMux()
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    fmt.Fprintln(w, "user", id)
})
http.ListenAndServe(":8080", mux)

That's enough for many services — no third-party router required.

The HTTP client

The same package gives you an HTTP client.

resp, err := http.Get("https://api.github.com/zen")
if err != nil { return err }
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))

For more control (timeouts, custom transports), build your own &http.Client{} and use client.Do(req). Always set a timeout on production clients — the default has none.

client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get(url)

Middleware

A middleware is just a function that takes a handler and returns a handler. It's the standard way to add logging, auth, recovery, request IDs, etc.:

func withLogging(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
    })
}

http.ListenAndServe(":8080", withLogging(mux))

Compose as many layers as you need. The first wrapper added is the outermost ("onion model").

The default `http.Client` (i.e. `http.Get`) has what timeout?