Good cake is the one you can easily slice into parts with no crumbs falling apart. That's all this project is about: 3 simple parts, no nasty additives. In and I've explained the basics of setting up golang project using docker, creating configurable server, interacting with DB, adding routes and handlers. part 1 part 2 What happens when multiple handlers have to reuse same logic? What is the most elegant way of solving this issue? This is where the middleware comes into play and will be my main focus here. The idea of a middleware in context of route handlers is that we construct our handlers from multiple, small functions - middlewares. That allows easy code re-usability and handlers become much easier to reason about. I'll continue working on resource from previous posts by adding additional logic to it. One common thing each handler might have is request logging. Before implementing it, there's a small utility function that will help me construct handlers: users middleware ( ) Middleware { (m) == { f } m[ ](Chain(f, m[ :]...)) } // pkg/middleware/middleware.go package import "github.com/julienschmidt/httprouter" type . . func (httprouter.Handle) httprouter Handle func Chain (f httprouter.Handle, m ...Middleware) httprouter Handle if len 0 return return 0 1 The function will accept any number of functions of a Middleware type and call them one by one giving us full control of the flow, more on that later. Now that I have this in place, I'll add simple request logging middleware. Since it's going to be reused in multiple handlers, I'll put it in pkg/middleware directory. All handler specific middleware will live next to handler itself: Chain middleware ( ) { { logger.Info.Printf( , r.RemoteAddr, r.Proto, r.Method, r.URL.RequestURI()) next(w, r, p) } } // pkg/middleware/logging.go package import "net/http" "github.com/boilerplate/pkg/logger" "github.com/julienschmidt/httprouter" . func LogRequest (next httprouter.Handle) httprouter Handle return func (w http.ResponseWriter, r *http.Request, p httprouter.Params) "%s - %s %s %s" Simple higher order function that accepts and returns function of a type and reads some data from request in between. Next I plan to accept user id as url parameter and I want to make sure it's a valid numeric string. If it's not, I'll simply respond to client with status code 412. Sounds like this logic can be implemented as a middleware: httprouter.Handle getuser ( ) { { uid := p.ByName( ) id, err := strconv.Atoi(uid) err != { w.WriteHeader(http.StatusPreconditionFailed) fmt.Fprintf(w, ) } ctx := context.WithValue(r.Context(), models.CtxKey( ), id) r = r.WithContext(ctx) next(w, r, p) } } // cmd/api/handlers/getuser/validarequest.go package import "context" "fmt" "net/http" "strconv" "github.com/boilerplate/cmd/api/models" "github.com/julienschmidt/httprouter" . func validateRequest (next httprouter.Handle) httprouter Handle return func (w http.ResponseWriter, r *http.Request, p httprouter.Params) "id" if nil "malformed id" return "userid" Same principle as in logging middleware. Context is a standard way of sharing data between middleware and I added user id to it for demonstration purposes. If client sends malformed id, I'm skipping all following steps in my middleware chain by simply not calling the function and responding to client directly from here. next Next step is handler itself: getuser.Do getuser ( ) { { r.Body.Close() id := r.Context().Value(models.CtxKey( )) user := &models.User{ID: id.( )} err := user.GetByID(r.Context(), app); err != { errors.Is(err, sql.ErrNoRows) { w.WriteHeader(http.StatusPreconditionFailed) fmt.Fprintf(w, ) } w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, ) } w.Header().Set( , ) response, _ := json.Marshal(user) w.Write(response) } } { mdw := []middleware.Middleware{ middleware.LogRequest, validateRequest, } middleware.Chain(getUser(app), mdw...) } // cmd/api/handlers/getuser/getuser.go package import "database/sql" "encoding/json" "errors" "fmt" "net/http" "github.com/boilerplate/cmd/api/models" "github.com/boilerplate/pkg/application" "github.com/boilerplate/pkg/middleware" "github.com/julienschmidt/httprouter" . func getUser (app *application.Application) httprouter Handle return func (w http.ResponseWriter, r *http.Request, p httprouter.Params) defer "userid" int if nil if "user does not exist" return "Oops" return "Content-Type" "application/json" . func Do (app *application.Application) httprouter Handle return I first create a slice of all required middleware which later becomes a variadic function parameter. is implemented in a way that it calls functions right to left so first is going to be called followed by validateRequest and . middleware.Chain LogRequest getUser Few things to point out here. Notice that middleware in slice has slightly different signature than . This is because is our last step in the chain and it will not be calling function to jump to next steps of a chain. Second thing is how I read value from context. Let's start the service before testing the handler: mdw getUser getUser next docker-compose up --build Now let's make a request with as user id. Since is not numeric string, I'd expect message in response: foobar foobar malformed id Now, let's try valid id. Currently I have no record with id in users table so would expect message: 2 user does not exist You can find whole project . I really hope this will be a good starting point for you and help building new, exciting things. here Stay safe, eat cake and keep coding!