# HTTP Servers With net/http — Go (Golang)

Source: https://www.skillbyai.com/en/go/s-http

> Routing with method and path patterns.

## ServeMux patterns and handlers

`net/http` provides production-grade HTTP servers and clients. Since Go 1.22 the built-in `ServeMux` supports **method and wildcard patterns** such as `"GET /products/{id}"`, read with `r.PathValue("id")`, and automatically answers 405 Method Not Allowed for other methods. Handlers write status, headers and body through `http.ResponseWriter`. `httptest` runs real servers on random ports for tests. In production, set server timeouts (ReadHeaderTimeout and friends) and shut down gracefully.

## HTTP, JSON and time out of the box

Go's standard library covers most needs of a web service without extra dependencies.

![Three ideas: net/http, encoding/json, time.](assets/figures/go/section-6-map.svg) — Figure 6.1 — HTTP, JSON and time.

## A product endpoint tested with httptest, run

I ran this with Go 1.27.1 (`go run .` in a module named demo, standard library only). The server returns JSON for product 1, a 404 for an unknown id, and 405 for a POST because the pattern only allows GET.

```go
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
)

type Product struct {
	ID    int     `json:"id"`
	Name  string  `json:"name"`
	Price float64 `json:"price"`
}

func newMux() *http.ServeMux {
	mux := http.NewServeMux()
	products := map[string]Product{"1": {1, "Notebook", 120}}
	mux.HandleFunc("GET /products/{id}", func(w http.ResponseWriter, r *http.Request) { // Go 1.22+ patterns
		p, ok := products[r.PathValue("id")]
		if !ok {
			http.Error(w, "product not found", http.StatusNotFound)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(p)
	})
	return mux
}

func main() {
	srv := httptest.NewServer(newMux()) // a real HTTP server on a random local port
	defer srv.Close()
	for _, path := range []string{"/products/1", "/products/9"} {
		resp, err := http.Get(srv.URL + path)
		if err != nil {
			panic(err)
		}
		body, _ := io.ReadAll(resp.Body)
		resp.Body.Close()
		fmt.Printf("GET %s -> %d %s", path, resp.StatusCode, body)
	}
	resp, _ := http.Post(srv.URL+"/products/1", "application/json", nil)
	fmt.Println("POST /products/1 ->", resp.StatusCode)
}
```

Output:

```
GET /products/1 -> 200 {"id":1,"name":"Notebook","price":120}
GET /products/9 -> 404 product not found
POST /products/1 -> 405
```

## Never use the default server without timeouts

Create an http.Server with ReadHeaderTimeout, ReadTimeout and WriteTimeout to protect against slow clients.

**Quiz:** What does the pattern "GET /products/{id}" do with a POST request?

- [x] The mux responds 405 Method Not Allowed
- [ ] Calls the handler anyway
- [ ] Returns 404 always
- [ ] Crashes the server

*Answer:* The mux responds 405 Method Not Allowed. Method-aware routing since Go 1.22.
