# Echo

> Echo is a high performance, extensible, minimalist Go web framework built on the standard `net/http` library. Echo adds a fast radix-tree router, request binding with pluggable validator, a deep middleware ecosystem, centralized error handling, and template rendering on top of the standard library.

Import path: `github.com/labstack/echo/v5`
Go version: 1.25+
Handler signature: `func(c *echo.Context) error`

## Context

`Context` is a concrete struct passed by pointer to all handlers.

```go
type Context struct { ... }

// Handlers receive *echo.Context
func handler(c *echo.Context) error { ... }
```

### Context methods

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Context store (type-safe)

```go
c.Set("user", user)
val, err := echo.ContextGet[*User](c, "user")
count, err := echo.ContextGetOr[int](c, "count", 0)
```

## Echo struct

```go
type Echo struct {
    Binder           Binder
    Filesystem       fs.FS
    Renderer         Renderer
    Validator        Validator
    JSONSerializer   JSONSerializer
    IPExtractor      IPExtractor
    OnAddRoute       func(route Route) error
    HTTPErrorHandler HTTPErrorHandler
    Logger           *slog.Logger
}
```

Constructor: `echo.New() *Echo` or `echo.NewWithConfig(config Config) *Echo`

## Echo methods

### Routing

```go
e.GET(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
e.POST(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
e.PUT(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
e.DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
e.PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
e.HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
e.OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
e.ANY(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
e.Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) Routes
e.RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
e.AddRoute(route Route) (RouteInfo, error)
```

### Middleware

```go
e.Use(middleware ...MiddlewareFunc)
e.Pre(middleware ...MiddlewareFunc)
e.Middlewares() []MiddlewareFunc
e.PreMiddlewares() []MiddlewareFunc
```

### Groups

```go
e.Group(prefix string, m ...MiddlewareFunc) *Group
```

### Static files

```go
e.Static(pathPrefix, fsRoot string, m ...MiddlewareFunc) RouteInfo
e.StaticFS(pathPrefix string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo
e.File(path, file string, m ...MiddlewareFunc) RouteInfo
e.FileFS(file string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo
```

### Server

```go
e.Start(address string) error
e.StartTLS(address string, certFile, keyFile any) error
```

```go
type StartConfig struct {
    Address          string
    HideBanner       bool
    HidePort         bool
    CertFilesystem   fs.FS
    TLSConfig        *tls.Config
    ListenerNetwork  string
    ListenerAddrFunc func(addr net.Addr)
    GracefulTimeout  time.Duration
    OnShutdownError  func(err error)
    BeforeServeFunc  func(s *http.Server) error
}

// StartConfig.Start and StartConfig.StartTLS for graceful shutdown
sc := echo.StartConfig{Address: ":8080", GracefulTimeout: 10 * time.Second}
sc.Start(ctx, e)
```

## Error handling

```go
// HTTPError with string message
err := echo.NewHTTPError(http.StatusBadRequest, "invalid input")

// Custom error handler (params: context pointer, then error)
e.HTTPErrorHandler = func(c *echo.Context, err error) {
    // custom error handling
}

// Default error handler factory (exposeError bool)
e.HTTPErrorHandler = echo.DefaultHTTPErrorHandler(true)
```

```go
echo.ErrBadRequest
echo.ErrValidatorNotRegistered
echo.ErrInvalidKeyType
echo.ErrNonExistentKey
```

## Logging

Echo uses the standard library `log/slog` package.

```go
e.Logger = slog.Default()
c.Logger().Info("message", "key", "value")

// Custom logger
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
e.Logger = logger
```

## Router

```go
// Router interface with DefaultRouter implementation
func NewRouter(config RouterConfig) *DefaultRouter
func (e *Echo) Router() Router

// Thread-safe routing
func NewConcurrentRouter(r Router) Router
```

## RouteInfo

```go
type RouteInfo struct {
    Name       string
    Method     string
    Path       string
    Parameters []string
}

// Routes type with filtering methods
type Routes []RouteInfo
// Methods: FilterByMethod, FilterByName, FilterByPath, FindByMethodPath, Reverse
```

## PathValues

```go
type PathValue struct {
    Name  string
    Value string
}
type PathValues []PathValue

func (p PathValues) Get(name string) (string, bool)
func (p PathValues) GetOr(name string, defaultValue string) string
```

## Generic helper functions

```go
echo.PathParam[T](c *Context, name string, opts ...any) (T, error)
echo.PathParamOr[T](c *Context, name string, defaultValue T, opts ...any) (T, error)
echo.QueryParam[T](c *Context, key string, opts ...any) (T, error)
echo.QueryParamOr[T](c *Context, key string, defaultValue T, opts ...any) (T, error)
echo.QueryParams[T](c *Context, key string, opts ...any) ([]T, error)
echo.QueryParamsOr[T](c *Context, key string, defaultValue []T, opts ...any) ([]T, error)
echo.FormValue[T](c *Context, key string, opts ...any) (T, error)
echo.FormValueOr[T](c *Context, key string, defaultValue T, opts ...any) (T, error)
echo.FormValues[T](c *Context, key string, opts ...any) ([]T, error)
echo.FormValuesOr[T](c *Context, key string, defaultValue []T, opts ...any) ([]T, error)
echo.ParseValue[T](value string, opts ...any) (T, error)
echo.ParseValueOr[T](value string, defaultValue T, opts ...any) (T, error)
echo.ParseValues[T](values []string, opts ...any) ([]T, error)
echo.ParseValuesOr[T](values []string, defaultValue []T, opts ...any) ([]T, error)
echo.ContextGet[T](c *Context, key string) (T, error)
echo.ContextGetOr[T](c *Context, key string, defaultValue T) (T, error)
```

Supported generic types: bool, string, int/int8/int16/int32/int64, uint/uint8/uint16/uint32/uint64, float32/float64, time.Time, time.Duration, BindUnmarshaler, encoding.TextUnmarshaler, json.Unmarshaler

## Binding functions

```go
echo.BindBody(c *Context, target any) error
echo.BindHeaders(c *Context, target any) error
echo.BindPathValues(c *Context, target any) error
echo.BindQueryParams(c *Context, target any) error
```

### Binder helpers

```go
echo.PathValuesBinder(c *Context) *ValueBinder
echo.QueryParamsBinder(c *Context) *ValueBinder
echo.FormFieldBinder(c *Context) *ValueBinder
```

## Group methods

```go
g.Use(m ...MiddlewareFunc)
g.Group(prefix string, m ...MiddlewareFunc) *Group
g.GET(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
g.POST(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
g.PUT(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
g.DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
g.PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
g.ANY(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo
g.Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) Routes
g.Static(pathPrefix, fsRoot string, m ...MiddlewareFunc) RouteInfo
g.StaticFS(pathPrefix string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo
g.File(path, file string, m ...MiddlewareFunc) RouteInfo
g.FileFS(file string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo
```

## Built-in middleware

| Middleware | Description |
|---|---|
| BasicAuth | HTTP basic authentication |
| BodyDump | Dumps request and response bodies |
| BodyLimit | Limits request body size |
| Compress | Gzip response compression |
| ContextTimeout | Request context timeout |
| CORS | Cross-origin resource sharing |
| CSRF | Cross-site request forgery protection |
| Decompress | Gzip request decompression |
| KeyAuth | Key-based authentication |
| MethodOverride | HTTP method override |
| Proxy | Reverse proxy |
| RateLimiter | Rate limiting |
| Recover | Panic recovery |
| Redirect | HTTP redirect |
| RequestID | Request ID header |
| RequestLogger | Structured request logging |
| Rewrite | URL rewriting |
| Secure | Security headers |
| Slash | Trailing slash handling |
| Static | Static file serving |

## Wrapping stdlib handlers

```go
echo.WrapHandler(h http.Handler) HandlerFunc
echo.WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc
```

## Virtual host

```go
echo.NewVirtualHostHandler(vhosts map[string]*Echo) *Echo
```

## echotest package

```go
import "github.com/labstack/echo/v5/echotest"

echotest.LoadBytes(t *testing.T, name string, opts ...loadBytesOpts) []byte
echotest.TrimNewlineEnd(bytes []byte) []byte
echotest.ContextConfig{...}
echotest.MultipartForm{...}
echotest.MultipartFormFile{...}
```

## Complete server example

```go
package main

import (
    "context"
    "log/slog"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/labstack/echo/v5"
    "github.com/labstack/echo/v5/middleware"
)

func main() {
    e := echo.New()
    e.Use(middleware.RequestLogger())
    e.Use(middleware.Recover())

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

    e.GET("/users/:id", func(c *echo.Context) error {
        id, err := echo.PathParam[int](c, "id")
        if err != nil {
            return echo.NewHTTPError(http.StatusBadRequest, "invalid user id")
        }
        return c.JSON(http.StatusOK, map[string]int{"id": id})
    })

    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
    defer cancel()

    sc := echo.StartConfig{
        Address:        ":8080",
        GracefulTimeout: 10 * time.Second,
    }
    if err := sc.Start(ctx, e); err != nil {
        slog.Error("server error", "error", err)
    }
}
```