Skip to content

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/v4

Getting started

The smallest useful thing you can do with it, and what each part means.

Routes that return errors
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"))
}
Returning an error is the idiomatic path — Echo's central error handler turns it into a response. This keeps handlers short and makes it impossible to forget to write a response after a failure.
Binding with validation
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)
})
Unlike Gin, Echo does not bundle a validator — you register one. That is more setup but leaves the choice of validation library open.

Advanced usage

Where the library earns its place over a simpler alternative.

Custom error handler and middleware
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),
}))
One error handler means internal details are never leaked by accident from an individual route. The Committed check avoids writing a second response if a handler already started one.

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.