Middleware, Errors & Static Files
Global vs. Per-Route Middleware
e := echo.New()
e.Use(middleware.Logger()) // every incoming request
e.Use(middleware.Recover()) // panic -> 500 instead of crashing the process
e.Use(middleware.CORS())
// per-route: pass middleware after the handler
e.GET("/admin", adminHandler, authMiddleware)Middleware wraps the handler chain, executing in registration order — each typically calls next() before/after its own logic. middleware.Recover() isolates a panic to just that one request rather than crashing the whole server.
Centralized Error Handling
func getUser(c echo.Context) error {
user, err := findUser(c.Param("id"))
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, "user not found")
}
return c.JSON(http.StatusOK, user)
}
// main() — one place formats every error response consistently
e.HTTPErrorHandler = func(err error, c echo.Context) {
code := http.StatusInternalServerError
if he, ok := err.(*echo.HTTPError); ok {
code = he.Code
}
c.JSON(code, map[string]string{"error": err.Error()})
}Static Files
e.Static("/static", "assets") // serves files from ./assets under /static/*Why Echo Over Plain net/http
net/http alone is fully capable, but Echo reduces boilerplate: flexible path-param routing, a built-in middleware ecosystem (logging, recovery, CORS, rate limiting), and binding/response helpers — versus hand-assembling equivalents from separate packages.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free