What it is
Echo is a minimalist, high-performance Go web framework with a strong middleware system, automatic TLS via Let's Encrypt, and first-class data binding and validation. It emphasises an explicit, well-typed handler signature.
Echo handlers have the signature func(echo.Context) error, which routes every failure through one configurable HTTPErrorHandler. It ships middleware for logging, recovery, CORS, gzip, JWT and rate limiting, plus binding from JSON, XML, form data and path or query parameters.
Installation
go get github.com/labstack/echo/v4Getting started
The smallest useful thing you can do with it, and what each part means.
package main
import (
"net/http"
"github.com/labstack/echo/v4"
)
func main() {
e := echo.New()
e.GET("/books/:id", func(c echo.Context) error {
book, err := store.Find(c.Param("id"))
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, "book not found")
}
return c.JSON(http.StatusOK, book)
})
e.Logger.Fatal(e.Start(":8080"))
}type Book struct {
Title string `json:"title" validate:"required,max=200"`
Year int `json:"year" validate:"gte=1400"`
}
type Validator struct{ v *validator.Validate }
func (val *Validator) Validate(i interface{}) error {
return val.v.Struct(i)
}
e.Validator = &Validator{v: validator.New()}
e.POST("/books", func(c echo.Context) error {
var b Book
if err := c.Bind(&b); err != nil {
return err
}
if err := c.Validate(&b); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
return c.JSON(http.StatusCreated, b)
})Advanced usage
Where the library earns its place over a simpler alternative.
e.HTTPErrorHandler = func(err error, c echo.Context) {
code := http.StatusInternalServerError
msg := "internal error"
var he *echo.HTTPError
if errors.As(err, &he) {
code = he.Code
msg = fmt.Sprint(he.Message)
}
c.Logger().Error(err) // log the real cause, return the safe message
if !c.Response().Committed {
c.JSON(code, map[string]string{"error": msg})
}
}
e.Use(middleware.RequestID())
e.Use(middleware.Gzip())
e.Use(middleware.RateLimiterWithConfig(middleware.RateLimiterConfig{
Store: middleware.NewRateLimiterMemoryStore(20),
}))Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- echo: invalid Validator instance
- c.Validate was called before e.Validator was set. Assign a type implementing echo.Validator during startup.
- Response already committed
- Two writes to the same response. Check c.Response().Committed before writing in error handlers or late middleware.
Best practices
- Return errors from handlers rather than writing error responses inline — that is what the design is for.
- Replace the default HTTPErrorHandler so clients get a consistent error shape and internals stay hidden.
- Register a validator explicitly; Echo will silently skip validation if c.Validate is called without one configured.
- Use middleware.Recover() in production so a panic in one handler does not take down the server.
Background
Why it exists, and what it was reacting to.
Echo was created by Vishal Rana (LabStack) in 2015 as an alternative to Gin with a cleaner error-handling model. Its distinguishing choice is that handlers return an error, so failures propagate to a central handler instead of each route writing its own response.
