Quick Note: If you checked out my previous post on JWT authentication and noticed some rendering issues, those have now been fixed! Be sure to give it another look because these examples build on top of that tutorial. :)
Alright folks, we’ve got our Go API running, we’ve added JWT authentication, and we’ve even connected it to a PostgreSQL database. But we’re not done yet! This week, we’re going to take things up a notch and make our API smarter and more developer-friendly by adding custom middleware for logging and error handling.
Middleware is like a bouncer at your favorite club—it intercepts requests before they hit your API endpoints. You can have middleware that checks authentication (like we did with JWT), logs information, or handles errors when things go wrong.
Today, we’re going to build middleware that:
Let’s dive into it!
Logging is your best friend when it comes to debugging and understanding what’s happening in your API. We’re going to create a middleware that logs every request that comes through—method, URL, and time taken.
func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() // Log the method and the requested URL log.Printf("Started %s %s", r.Method, r.URL.Path) // Call the next handler in the chain next.ServeHTTP(w, r) // Log how long it took log.Printf("Completed in %v", time.Since(start)) }) }
For those who are interested in diving deeper into logging middleware, I recommend checking out Matt Silverlock’s fantastic guide on writing logging middleware in Go. He breaks down how to structure reusable middleware for various use cases like authentication, tracing, and of course, logging!
Let’s talk about errors. Errors happen, right? But rather than letting them cause a crash or send a vague error message, let’s handle them gracefully.
func errorHandlingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer func() { if err := recover(); err != nil { // Log the error and send a user-friendly message log.Printf("Error occurred: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } }() next.ServeHTTP(w, r) }) }
Now that we’ve built our logging and error-handling middleware, let’s hook them up to our API. We’ll apply them globally so every request gets logged and errors are caught.
func main() { db = connectDB() defer db.Close() r := mux.NewRouter() // Apply middleware globally r.Use(loggingMiddleware) r.Use(errorHandlingMiddleware) r.HandleFunc("/login", login).Methods("POST") r.Handle("/books", authenticate(http.HandlerFunc(getBooks))).Methods("GET") r.Handle("/books", authenticate(http.HandlerFunc(createBook))).Methods("POST") fmt.Println("Server started on port :8000") log.Fatal(http.ListenAndServe(":8000", r)) }
To make sure everything’s working, start up your API:
go run main.go
Now, try hitting any of your endpoints (like /books) and check your terminal. You should see logs like:
Started GET /books Completed in 1.2ms
And if there’s an error, you’ll see:
Error occurred: some error details
But your user will only see a clean "500 Internal Server Error" message. ?
Logging helps you track down bugs and monitor the behavior of your API. If something goes wrong, you’ll know exactly which endpoint was hit and how long the request took.
Error Handling prevents your API from crashing when something unexpected happens. Instead, it recovers gracefully and sends a clean error message to the client.
Next time, we’ll take things to the next level and dockerize our Go API! This will make your app portable and ready for deployment on any machine or cloud service. Get ready for some container magic! ?
The above is the detailed content of Adding Logging and Error Handling Middleware to Your Go API. For more information, please follow other related articles on the PHP Chinese website!