httpclient and (decorating) middleware in golang
07:12 10 Jun 2026

I am trying to grasp (and sofar failing) to exactly grasp how the following code example works. It wraps (decorates) a standard http.client with 2 middleware functions (Logger and BasicAuth) that are invoked when making client (RoundTripper) calls. The setup/initialization is as follows:

package decorator

import (
    "log"
    "net/http"
    "os"
)

// Setup initializes our ClientInterface
func Setup() *http.Client {
    c := http.Client{}

    t := Decorate(&http.Transport{},
        Logger(log.New(os.Stdout, "", 0)),
        BasicAuth("username", "password"),
    )
    c.Transport = t
    return &c
}

The problem is that I dont exactly understand what happens in the Decorate function.
The whole file containing the function is as follows:

package decorator

import "net/http"

// TransportFunc implements the RountTripper interface
type TransportFunc func(*http.Request) (*http.Response, error)

// RoundTrip just calls the original function
func (tf TransportFunc) RoundTrip(r *http.Request) (*http.Response, error) {
    return tf(r)
}

// Decorator is a convenience function to represent our
// middleware inner function
type Decorator func(http.RoundTripper) http.RoundTripper

// Decorate is a helper to wrap all the middleware
func Decorate(t http.RoundTripper, rts ...Decorator) http.RoundTripper {
    decorated := t
    for _, rt := range rts {
        decorated = rt(decorated)
    }
    return decorated
}

The 2 middleware functions are defined as follows:

package decorator

import (
    "log"
    "net/http"
    "time"
)

// Logger is one of our 'middleware' decorators
func Logger(l *log.Logger) Decorator {
    return func(c http.RoundTripper) http.RoundTripper {
        return TransportFunc(func(r *http.Request) (*http.Response, error) {
            start := time.Now()
            l.Printf("started request to %s at %s", r.URL, start.Format("2006-01-02 15:04:05"))
            resp, err := c.RoundTrip(r)
            l.Printf("completed request to %s in %s", r.URL, time.Since(start))
            return resp, err
        })
    }
}

// BasicAuth is another of our 'middleware' decorators
func BasicAuth(username, password string) Decorator {
    return func(c http.RoundTripper) http.RoundTripper {
        return TransportFunc(func(r *http.Request) (*http.Response, error) {
            r.SetBasicAuth(username, password)
            resp, err := c.RoundTrip(r)
            return resp, err
        })
    }
}

I understand that in Setup() the Transport of the http.Client is augmented with "configuration" of the 2 middleware functions but don't understand how the "Decorate" functions works and how the decorators exactly are added to the transport. (I see in the debugger that under var t at some point in time there is a \data struct that seems to include references to the logger function and the user/password combination but have no idea where this \data comes from)

Hope this describes my problem fully if not please ask for additional info

thanks

go decorator http.client