Echo Framework Reference

Echo v4 Go framework: routing, groups, middleware, data binding, validation, error handling, and graceful shutdown.

1. Setup & Routing

package main

import (
    "net/http"
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)

func main() {
    e := echo.New()
    e.HideBanner = true

    // Middleware
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    e.Use(middleware.RequestID())

    // Routes
    e.GET("/", func(c echo.Context) error {
        return c.JSON(http.StatusOK, echo.Map{"message": "hello"})
    })

    // Route groups
    api := e.Group("/api")
    api.Use(middleware.JWT([]byte("secret")))
    {
        api.GET("/articles", listArticles)
        api.POST("/articles", createArticle)
        api.GET("/articles/:id", getArticle)
        api.PUT("/articles/:id", updateArticle)
        api.DELETE("/articles/:id", deleteArticle)
    }

    e.Logger.Fatal(e.Start(":8080"))
}

2. Binding & Validation

import "github.com/go-playground/validator/v10"

type CreateArticleRequest struct {
    Title   string `json:"title"   validate:"required,min=5,max=300"`
    Content string `json:"content" validate:"required,min=50"`
    Status  string `json:"status"  validate:"omitempty,oneof=draft published"`
}

// Custom validator
type CustomValidator struct {
    validator *validator.Validate
}

func (cv *CustomValidator) Validate(i interface{}) error {
    if err := cv.validator.Struct(i); err != nil {
        return echo.NewHTTPError(http.StatusUnprocessableEntity, err.Error())
    }
    return nil
}

// Register
e.Validator = &CustomValidator{validator: validator.New()}

// Handler
func createArticle(c echo.Context) error {
    req := new(CreateArticleRequest)
    if err := c.Bind(req); err != nil {
        return err
    }
    if err := c.Validate(req); err != nil {
        return err
    }
    // process...
    return c.JSON(http.StatusCreated, req)
}

3. Context Methods

func handler(c echo.Context) error {
    // Path parameters
    id := c.Param("id")

    // Query parameters
    page := c.QueryParam("page")
    page = c.QueryParamWithDefault("page", "1")  // Echo v5

    // Headers
    auth := c.Request().Header.Get("Authorization")

    // Set / Get values
    c.Set("user", currentUser)
    user := c.Get("user").(*User)

    // Response helpers
    return c.JSON(200, map[string]any{"id": id})
    return c.String(200, "hello")
    return c.HTML(200, "<h1>Hello</h1>")
    return c.Redirect(301, "/new-path")
    return c.File("./static/index.html")
    return c.Stream(200, "text/event-stream", reader)
    return c.Blob(200, "image/png", bytes)

    // Error helpers
    return echo.NewHTTPError(404, "Not found")
    return echo.NewHTTPError(422, map[string]any{"errors": errs})
}

4. Custom Middleware

// Middleware factory
func RateLimiter(rps int) echo.MiddlewareFunc {
    limiter := rate.NewLimiter(rate.Limit(rps), rps)
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            if !limiter.Allow() {
                return echo.NewHTTPError(http.StatusTooManyRequests, "rate limit exceeded")
            }
            return next(c)
        }
    }
}

// CORS
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
    AllowOrigins: []string{"https://app.example.com"},
    AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete},
    AllowHeaders: []string{echo.HeaderAuthorization, echo.HeaderContentType},
    AllowCredentials: true,
}))

// JWT
e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
    SigningKey: []byte(os.Getenv("JWT_SECRET")),
    ContextKey: "jwt_claims",
    TokenLookup: "header:Authorization:Bearer ",
}))

5. Error Handling

// Custom HTTP error handler
e.HTTPErrorHandler = func(err error, c echo.Context) {
    var he *echo.HTTPError
    if errors.As(err, &he) {
        c.JSON(he.Code, echo.Map{
            "error": he.Message,
        })
        return
    }
    c.Logger().Error(err)
    c.JSON(http.StatusInternalServerError, echo.Map{
        "error": "Internal server error",
    })
}

// Graceful shutdown
go func() {
    if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
        e.Logger.Fatal("shutting down the server")
    }
}()

quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := e.Shutdown(ctx); err != nil {
    e.Logger.Fatal(err)
}

6. Built-in Middleware Reference

MiddlewarePackagePurpose
Logger()middlewareRequest logging
Recover()middlewarePanic recovery
CORS()middlewareCross-origin headers
JWT(key)middlewareJWT authentication
BasicAuth(fn)middlewareHTTP Basic Auth
KeyAuth(fn)middlewareAPI key auth
RateLimitermiddlewareRate limiting
Gzip()middlewareGzip compression
Secure()middlewareSecurity headers
RequestID()middlewareX-Request-ID header
BodyLimit(size)middlewareLimit request size