Routing: Multiple Endpoints & Methods
π― Your mission: Lesson 0001 gave you one route. Real APIs need many β GET /users, POST /users, GET /users/42. This lesson teaches routing by path and HTTP method.
What youβll build: A server with three endpoints:
GET /β welcome messageGET /healthβ{"status":"ok"}POST /echoβ reads request body, sends it back
Pointers: what *http.Request means
The * is a pointer β instead of copying the Request, Go passes a memory address. For now: pointer = βdonβt copy, just point to the original.β β Tour of Go: Pointers
http.NewServeMux β your own router
In lesson 0001 we passed nil to ListenAndServe. That uses a hidden default router. Better: create your own ServeMux and pass it explicitly. β ServeMux docs
Checking r.Method
HandleFunc matches on path, not method. To restrict to GET, POST, etc., check r.Method inside the handler. Go provides constants: http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete. β method constants
Request: GET /health β ServeMux checks registered paths β Matches "/health" β calls healthHandler(w, r)
Follow each step in order. Click Done β after completing it to unlock the next one.
mkdir -p ~/dev/learn/go-lang/lessons/0002-routing
cd ~/dev/learn/go-lang/lessons/0002-routing
go mod init routing-serverCreate main.go:
package main
import (
"io"
"log"
"net/http"
)
func welcomeHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Welcome to the routing server!\n"))
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"status":"ok"}`))
}
func echoHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read body", http.StatusBadRequest)
return
}
defer r.Body.Close()
w.Write(body)
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", welcomeHandler)
mux.HandleFunc("/health", healthHandler)
mux.HandleFunc("/echo", echoHandler)
log.Println("Server starting on http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", mux))
}Whatβs new here?
mux := http.NewServeMux()β explicit router, no morenilw.Header().Set(β¦)β set response headersr.Method != http.MethodPostβ reject wrong methodsio.ReadAll(r.Body)β read the request bodydefer r.Body.Close()β cleanup when the function returnshttp.Error(w, msg, code)β send error response with status code
Start the server:
go run main.goIn a second terminal, test each endpoint:
# GET /
curl http://localhost:8080/
# GET /health
curl http://localhost:8080/health
# POST /echo
curl -X POST -d "hello world" http://localhost:8080/echo
# GET /echo (should return 405 Method Not Allowed)
curl http://localhost:8080/echoPress Ctrl+C to stop the server when youβre done.
Check your understanding
Why pass mux to ListenAndServe instead of nil?
A request comes in as PUT /echo. What happens?
What does defer r.Body.Close() do?