Middleware
Middleware model comparison
Section titled “Middleware model comparison”Express middleware is (req, res, next) => void. You call next() to pass control to the next layer, or call res.json() to short-circuit. Echo middleware follows the same pipeline idea but uses Go’s error-return pattern and closures:
// Echo middleware signaturefunc(next echo.HandlerFunc) echo.HandlerFuncA middleware returns a new HandlerFunc that wraps next. Call next(c) to pass control forward, or return an error to abort.
// Express middlewarefunction requestLogger(req, res, next) { console.log(`${req.method} ${req.path}`); next();}app.use(requestLogger);// Echo middlewarefunc RequestLogger(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { fmt.Printf("%s %s\n", c.Request().Method, c.Request().URL.Path) return next(c) // pass control forward }}
e.Use(RequestLogger)Built-in middleware
Section titled “Built-in middleware”Echo ships several production-ready middlewares in the middleware sub-package.
// Express with popular packagesimport morgan from 'morgan';import cors from 'cors';
app.use(morgan('combined'));app.use(cors({ origin: '*' }));// No built-in recover — need express-async-errors or similar// Echo built-ins — no extra packages neededimport "github.com/labstack/echo/v4/middleware"
e.Use(middleware.Logger()) // structured request log to stdoute.Use(middleware.Recover()) // catches panics, returns 500e.Use(middleware.CORS()) // default: allows all origins
// Restrict CORS to specific origins:e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ AllowOrigins: []string{"https://example.com"}, AllowMethods: []string{http.MethodGet, http.MethodPost},}))Middleware ordering
Section titled “Middleware ordering”Just like Express, order matters. Middleware registered first runs first. Logger and Recover should always be outermost so they see every request and catch every panic.
e.Use(middleware.Logger()) // 1st — log all requestse.Use(middleware.Recover()) // 2nd — catch panics from any later handlere.Use(authMiddleware) // 3rd — auth checks after logging is set upRoute-level and group-level middleware
Section titled “Route-level and group-level middleware”In Express you pass middleware directly to a route. Echo supports the same with .Use on a group.
// Express — per-route middlewareapp.get('/admin', authMiddleware, adminHandler);
// Express — router-levelconst adminRouter = express.Router();adminRouter.use(authMiddleware);adminRouter.get('/', adminHandler);app.use('/admin', adminRouter);// Echo — per-route middlewaree.GET("/admin", adminHandler, authMiddleware)
// Echo — group-level middlewareadmin := e.Group("/admin")admin.Use(authMiddleware)admin.GET("", adminHandler)admin.GET("/users", adminUsersHandler)Writing a custom auth middleware
Section titled “Writing a custom auth middleware”// Express JWT-like auth middlewarefunction requireAuth(req, res, next) { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ message: 'unauthorized' }); try { req.user = verifyToken(token); next(); } catch { res.status(401).json({ message: 'invalid token' }); }}// Echo custom auth middlewarefunc RequireAuth(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { header := c.Request().Header.Get("Authorization") if header == "" { return echo.NewHTTPError(http.StatusUnauthorized, "missing token") } parts := strings.SplitN(header, " ", 2) if len(parts) != 2 || parts[0] != "Bearer" { return echo.NewHTTPError(http.StatusUnauthorized, "bad token format") } claims, err := verifyToken(parts[1]) if err != nil { return echo.NewHTTPError(http.StatusUnauthorized, "invalid token") } // Store claims in context for downstream handlers c.Set("user", claims) return next(c) }}Run this locally — it needs the Echo module and a network port.