Master Go Middleware: Build Reusable Handlers with Negroni
This article explains the concept of middleware, demonstrates how to implement it in Go using the net/http Handler interface and the Negroni library, and provides complete code examples for logging and authentication middleware chains.
This article introduces the concept of middleware and provides a Go example, with a link to the complete code at the end.
What is Middleware?
If you are already familiar with middleware and just want to know how to practice, you can skip this part.
In the web world, middleware is code that runs for client‑initiated network requests and links the request to the next middleware, eventually reaching the target handler. Middleware is useful when you need to perform shared operations across multiple requests, such as authentication, performance logging, and data collection.
For example, suppose we have a service that returns a list of messages submitted by a user, and another service that returns the user's personal sensitive information. Only when the user is logged in can these services be accessed. Additionally, we want to record the execution time of each request from the client to the server. Authentication and logging code could be written together with the controller and duplicated across both services, but this can be split into four separate packages: one for logging (LOG), one for authentication (AUTH), one for returning messages (RETURN MSG), and one for returning user data (RETURN USER).
By writing these functionalities as middleware, we can easily chain the request processing and optionally return early at any middleware. For example, if a user sends a request without being logged in. The overall processing might look like the following:
[LOG] -> [AUTH] -> [RETURN MSG]
[LOG] -> [AUTH] -> [RETURN USER]Since LOG and AUTH are separated, we only need to write and test the code once.
Go Middleware
In Go, all network requests are handled by implementing the net/http package's Handler interface. For developers unfamiliar with Go, a Handler responds to requests, reads request parameters, and writes responses. Because Go requires this interface in its core functions, building packages or decorators around it is straightforward and reliable.
A quick way to create middleware is to wrap the net/http.Handler.ServeHTTP method: ServeHTTP(ResponseWriter, *Request) By adding an external function that takes a Handler parameter and returns a Handler using http.HandlerFunc:
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Code for the middleware...
next.ServeHTTP(w, r)
})
}The above function receives a Handler argument, linking the request to the final function. Since the return value is also a Handler, calls can be chained:
firstMiddleware(secondMiddleware(lastHandler))With this simple approach, middleware can be implemented effectively. Although not very robust, it works.
Next, I will use the Negroni package to simplify middleware creation. Of course you can implement it yourself. Negroni wraps a router and provides flexible middleware management.
Implementation – Wrapping Handlers
To keep it simple, below we implement a middleware that records request execution time using Negroni. First create a router, then wrap it with Negroni:
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/urfave/negroni"
)
func main() {
router := mux.NewRouter()
router.
Methods("GET").
Path("/").
HandlerFunc(endpointHandler)
n := negroni.New()
n.UseHandler(router)
err := http.ListenAndServe(":8080", n)
if err != nil {
panic(err)
}
}
func endpointHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("Endpoint handler called")
}The code shows creating a router with gorilla/mux, registering a GET service, creating a Negroni instance, and using it as a handler. Running the code and sending a GET request (e.g., curl localhost:8080/) will display “Endpoint handler called” to the client.
Middleware Implementation
Next we create middleware as a Handler. Negroni’s Handler interface is similar to net/http ’s but also supports linking to the next parameter:
type Handler interface {
ServeHTTP(
rw http.ResponseWriter,
r *http.Request,
next http.HandlerFunc,
)
}Let’s create a simple logging middleware:
package mw
import (
"fmt"
"net/http"
"time"
)
type Logger struct{}
func (l *Logger) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
fmt.Println("The logger middleware is executing!")
t := time.Now()
next.ServeHTTP(w, r)
fmt.Printf("Execution time: %s
", time.Now().Sub(t).String())
}The code demonstrates how to implement Negroni.Handler to log execution time. After the next handler finishes, the total execution time is recorded.
Add the logger middleware to the router and observe execution time:
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/johan-lejdung/go-microservice-middleware-guide/mw"
"github.com/urfave/negroni"
)
func main() {
router := mux.NewRouter()
router.
Methods("GET").
Path("/").
HandlerFunc(endpointHandler)
n := negroni.New()
n.Use(&mw.Logger{})
n.UseHandler(router)
err := http.ListenAndServe(":8080", n)
if err != nil {
panic(err)
}
}
func endpointHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("Endpoint handler called")
}Running the code produces logs like:
The logger middleware is executing!
Endpoint handler called
Execution time: 25.146µsThe first and third lines come from the middleware; the second line is from the endpoint handler. We have successfully implemented middleware.
Consider an authentication middleware (AuthMW) that blocks unauthorized requests. To add it to the chain, simply:
n.Use(&mw.Logger{})
n.Use(&mw.Auth{})We have built the first part of the middleware chain. This code will execute Logger before Auth. By creating middleware chains per router path and combining both methods, you gain powerful tools for routing and code execution.
Full code repository: https://github.com/johan-lejdung/go-microservice-middleware-guide
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
