Echo Go web framework. Covers routing, middleware, binding, context, and WebSocket. Use for high-performance, minimalist Go APIs. USE WHEN: user mentions "echo", "labstack echo", "go echo framework", asks about "echo middleware", "echo context", "echo binding", "echo websocket", "high performance go api", "echo router" DO NOT USE FOR: Gin projects - use `gin` instead, Fiber projects - use `fiber` instead, Chi projects - use `chi` instead, non-Go backends

GitHub
安装命令
npx skhub add claude-dev-suite/echo
Markdown
SKILL.md

Echo Core Knowledge

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: echo for comprehensive documentation.

Basic Setup

package main

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

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

    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })

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

Routing

Basic Routes

e := echo.New()

e.GET("/users", listUsers)
e.GET("/users/:id", getUser)
e.POST("/users", createUser)
e.PUT("/users/:id", updateUser)
e.DELETE("/users/:id", deleteUser)

// Any method
e.Any("/any", handleAny)

// Match specific methods
e.Match([]string{"GET", "POST"}, "/multi", handler)

Path Parameters

// Single parameter
e.GET("/users/:id", func(c echo.Context) error {
    id := c.Param("id")
    return c.JSON(http.StatusOK, map[string]string{"id": id})
})

// Multiple parameters
e.GET("/users/:userId/posts/:postId", func(c echo.Context) error {
    userId := c.Param("userId")
    postId := c.Param("postId")
    return c.JSON(http.StatusOK, map[string]string{
        "userId": userId,
        "postId": postId,
    })
})

Route Groups

api := e.Group("/api")

v1 := api.Group("/v1")
v1.GET("/users", listUsersV1)
v1.POST("/users", createUserV1)

v2 := api.Group("/v2")
v2.GET("/users", listUsersV2)
v2.POST("/users", createUserV2)

// Group with middleware
admin := api.Group("/admin", adminMiddleware)
admin.GET("/stats", getStats)

Context

Request Data

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

    // Query params
    page := c.QueryParam("page")
    pageInt, _ := strconv.Atoi(c.QueryParam("page"))

    // Form data
    name := c.FormValue("name")

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

    // Cookies
    cookie, _ := c.Cookie("session")

    return c.NoContent(http.StatusOK)
}

Binding

type CreateUserRequest struct {
    Name  string `json:"name" validate:"required"`
    Email string `json:"email" validate:"required,email"`
}

func createUser(c echo.Context) error {
    var req CreateUserRequest
    if err := c.Bind(&req); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }

    return c.JSON(http.StatusCreated, req)
}

// Query binding
type ListQuery struct {
    Page    int `query:"page"`
    PerPage int `query:"per_page"`
}

func listUsers(c echo.Context) error {
    var query ListQuery
    if err := c.Bind(&query); err != nil {
        return err
    }
    return c.JSON(http.StatusOK, query)
}

Validation

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

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.StatusBadRequest, err.Error())
    }
    return nil
}

func main() {
    e := echo.New()
    e.Validator = &CustomValidator{validator: validator.New()}

    e.POST("/users", createUser)
    e.Logger.Fatal(e.Start(":8080"))
}

func createUser(c echo.Context) error {
    var req CreateUserRequest
    if err := c.Bind(&req); err != nil {
        return err
    }
    if err := c.Validate(req); err != nil {
        return err
    }
    return c.JSON(http.StatusCreated, req)
}

Middleware

Built-in Middleware

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

e := echo.New()

e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(middleware.RequestID())
e.Use(middleware.Gzip())

// CORS
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
    AllowOrigins: []string{"https://example.com"},
    AllowMethods: []string{echo.GET, echo.POST, echo.PUT, echo.DELETE},
    AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAuthorization},
}))

// Rate limiter
e.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(20)))

Custom Middleware

func TimingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        start := time.Now()
        err := next(c)
        duration := time.Since(start)
        c.Response().Header().Set("X-Response-Time", duration.String())
        return err
    }
}

e.Use(TimingMiddleware)

Authentication Middleware

func AuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        auth := c.Request().Header.Get("Authorization")

        if auth == "" {
            return echo.NewHTTPError(http.StatusUnauthorized, "missing token")
        }

        if !strings.HasPrefix(auth, "Bearer ") {
            return echo.NewHTTPError(http.StatusUnauthorized, "invalid format")
        }

        token := auth[7:]
        user, err := validateToken(token)
        if err != nil {
            return echo.NewHTTPError(http.StatusUnauthorized, "invalid token")
        }

        c.Set("user", user)
        return next(c)
    }
}

protected := e.Group("/api", AuthMiddleware)
protected.GET("/me", getMe)

func getMe(c echo.Context) error {
    user := c.Get("user").(*User)
    return c.JSON(http.StatusOK, user)
}

JWT Middleware

import "github.com/labstack/echo-jwt/v4"

e.Use(echojwt.WithConfig(echojwt.Config{
    SigningKey: []byte("secret"),
    NewClaimsFunc: func(c echo.Context) jwt.Claims {
        return new(jwtCustomClaims)
    },
}))

Response

Response Types

// JSON
c.JSON(http.StatusOK, map[string]string{"message": "hello"})

// String
c.String(http.StatusOK, "Hello, World!")

// HTML
c.HTML(http.StatusOK, "<h1>Hello</h1>")

// XML
c.XML(http.StatusOK, data)

// Blob
c.Blob(http.StatusOK, "application/octet-stream", bytes)

// File
c.File("./static/file.txt")

// Redirect
c.Redirect(http.StatusMovedPermanently, "https://example.com")

// No Content
c.NoContent(http.StatusNoContent)

WebSocket

import (
    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool { return true },
}

e.GET("/ws", func(c echo.Context) error {
    ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
    if err != nil {
        return err
    }
    defer ws.Close()

    for {
        mt, msg, err := ws.ReadMessage()
        if err != nil {
            break
        }

        if err := ws.WriteMessage(mt, msg); err != nil {
            break
        }
    }
    return nil
})

Error Handling

HTTP Errors

func getUser(c echo.Context) error {
    id := c.Param("id")
    user, err := findUser(id)
    if err != nil {
        return echo.NewHTTPError(http.StatusNotFound, "user not found")
    }
    return c.JSON(http.StatusOK, user)
}

Custom Error Handler

func customHTTPErrorHandler(err error, c echo.Context) {
    code := http.StatusInternalServerError
    message := "Internal Server Error"

    if he, ok := err.(*echo.HTTPError); ok {
        code = he.Code
        message = he.Message.(string)
    }

    c.Logger().Error(err)
    c.JSON(code, map[string]interface{}{
        "error":   http.StatusText(code),
        "message": message,
    })
}

e.HTTPErrorHandler = customHTTPErrorHandler

Production Readiness

Health Checks

e.GET("/health", func(c echo.Context) error {
    return c.JSON(http.StatusOK, map[string]string{"status": "healthy"})
})

e.GET("/ready", func(c echo.Context) error {
    ctx, cancel := context.WithTimeout(c.Request().Context(), 2*time.Second)
    defer cancel()

    if err := db.PingContext(ctx); err != nil {
        return c.JSON(http.StatusServiceUnavailable, map[string]string{
            "status":   "not ready",
            "database": "disconnected",
        })
    }

    return c.JSON(http.StatusOK, map[string]string{
        "status":   "ready",
        "database": "connected",
    })
})

Graceful Shutdown

func main() {
    e := echo.New()
    e.GET("/", handler)

    go func() {
        if err := e.Start(":8080"); 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(), 30*time.Second)
    defer cancel()

    if err := e.Shutdown(ctx); err != nil {
        e.Logger.Fatal(err)
    }
}

Checklist

  • Recovery middleware enabled
  • CORS configured
  • Rate limiting enabled
  • Request validation
  • Custom error handler
  • Health/readiness endpoints
  • Graceful shutdown
  • Structured logging

When NOT to Use This Skill

  • Gin projects - Gin has different context and binding API
  • Fiber projects - Fiber is Express-like with different patterns
  • Chi projects - Chi is more stdlib-compatible
  • Simple stdlib servers - Echo adds framework overhead
  • gRPC-only services - Use gRPC framework directly

Anti-Patterns

Anti-PatternWhy It's BadSolution
Not returning from middlewareHandler executes anywayReturn early on auth failures
Using c.JSON() without statusUnclear response codesAlways specify status code first
Missing error handlingPanics in productionUse echo.NewHTTPError()
Global validator not setNo validationSet e.Validator with custom validator
Not using groupsFlat route structureGroup routes with e.Group()
Ignoring context cancellationLong-running requestsCheck c.Request().Context()

Quick Troubleshooting

ProblemDiagnosisFix
Middleware not executingWrong positionPlace before route registration
Validation not workingNo validator setSet e.Validator with go-playground/validator
CORS errorsNot configuredUse middleware.CORS()
Route not matchingParameter mismatchCheck path parameter syntax :id
WebSocket handshake failsWrong upgrade headerUse gorilla/websocket upgrader
Panic on nil pointerMissing error checkCheck c.Bind() return value

Reference Documentation

发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

2026年9月24日

分类

未分类

许可证

MIT

源路径

skills/backend-frameworks/echo

默认分支

main

最新提交

9496306

Tree SHA

fe4e2f1