go 54 lines · 8 steps

Composing HTTP middleware in Go

A middleware type and a Chain helper let you wrap an http.Handler in reusable layers for logging and auth.

Explained by highlit
1package server
2 
3import (
4 "context"
5 "log"
6 "net/http"
7 "time"
8)
9 
10type Middleware func(http.Handler) http.Handler
11 
12func Chain(h http.Handler, middlewares ...Middleware) http.Handler {
13 for i := len(middlewares) - 1; i >= 0; i-- {
14 h = middlewares[i](h)
15 }
16 return h
17}
18 
19func Logging(logger *log.Logger) Middleware {
20 return func(next http.Handler) http.Handler {
21 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
22 start := time.Now()
23 rw := &statusRecorder{ResponseWriter: w, status: http.StatusOK}
24 next.ServeHTTP(rw, r)
25 logger.Printf("%s %s %d %s", r.Method, r.URL.Path, rw.status, time.Since(start))
26 })
27 }
28}
29 
30func RequireAuth(next http.Handler) http.Handler {
31 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
32 token := r.Header.Get("Authorization")
33 if token == "" {
34 http.Error(w, "unauthorized", http.StatusUnauthorized)
35 return
36 }
37 ctx := context.WithValue(r.Context(), userKey, token)
38 next.ServeHTTP(w, r.WithContext(ctx))
39 })
40}
41 
42type statusRecorder struct {
43 http.ResponseWriter
44 status int
45}
46 
47func (r *statusRecorder) WriteHeader(code int) {
48 r.status = code
49 r.ResponseWriter.WriteHeader(code)
50}
51 
52type contextKey string
53 
54const userKey contextKey = "user"
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Middleware is just a function that takes a handler and returns a wrapped handler, so layers compose freely.
  2. 2Wrapping the ResponseWriter is the standard trick for observing status codes the handler sets.
  3. 3context.WithValue passes per-request data down the chain without changing handler signatures.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Composing HTTP middleware in Go — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code