{"owner":"labstack","repo":"echo","hasSkills":true,"totalSkillsCount":3,"totalTokensCount":5128,"categories":["claude-rule","root-instruction"],"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md","llms.txt","llms-full.txt"],"skills":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## About This Project\n\nEcho is a high performance, minimalist Go web framework. This is the main repository for Echo v5, which is available as a Go module at `github.com/labstack/echo/v5`.\n\n## Development Commands\n\nThe project uses a Makefile for common development tasks:\n\n- `make check` - Run linting, vetting, and race condition tests (default target)\n- `make init` - Install required linting tools (golint, staticcheck)\n- `make lint` - Run staticcheck and golint\n- `make vet` - Run go vet\n- `make test` - Run short tests\n- `make race` - Run tests with race detector\n- `make benchmark` - Run benchmarks\n\nExample commands for development:\n```bash\n# Setup development environment\nmake init\n\n# Run all checks (lint, vet, race)\nmake check\n\n# Run specific tests\ngo test ./middleware/...\ngo test -race ./...\n\n# Run benchmarks\nmake benchmark\n```\n\n## Code Architecture\n\n### Core Components\n\n**Echo Instance (`echo.go`)**\n- The `Echo` struct is the top-level framework instance\n- Contains router, middleware stacks, and server configuration\n- Not goroutine-safe for mutations after server start\n\n**Context (`context.go`)**\n- The `Context` struct represents HTTP request/response context\n- Provides methods for request/response handling, path parameters, data binding\n- Core abstraction for request processing\n\n**Router (`router.go`)**\n- Radix tree-based HTTP router with smart route prioritization\n- Supports static routes, parameterized routes (`/users/:id`), and wildcard routes (`/static/*`)\n- Each HTTP method has its own routing tree\n\n**Middleware (`middleware/`)**\n- Extensive middleware system with 50+ built-in middlewares\n- Middleware can be applied at Echo, Group, or individual route level\n- Common middleware: Logger, Recover, CORS, JWT, Rate Limiting, etc.\n\n### Key Patterns\n\n**Middleware Chain**\n- Pre-middleware runs before routing\n- Regular middleware runs after routing but before handlers\n- Middleware functions have signature `func(next echo.HandlerFunc) echo.HandlerFunc`\n\n**Route Groups**\n- Routes can be grouped with common prefixes and middleware\n- Groups support nested sub-groups\n- Defined in `group.go`\n\n**Data Binding**\n- Automatic binding of request data (JSON, XML, form) to Go structs\n- Implemented in `binder.go` with support for custom binders\n\n**Error Handling**\n- Centralized error handling via `HTTPErrorHandler`\n- Automatic panic recovery with stack traces\n\n## File Organization\n\n- Root directory: Core Echo functionality (echo.go, context.go, router.go, etc.)\n- `middleware/`: All built-in middleware implementations\n- `_test/`: Test fixtures and utilities\n- `_fixture/`: Test data files\n\n## Code Style\n\n- Go code uses tabs for indentation (per .editorconfig)\n- Follows standard Go conventions and formatting\n- Uses gofmt, golint, and staticcheck for code quality\n\n## Testing\n\n- Standard Go testing with `testing` package\n- Tests include unit tests, integration tests, and benchmarks\n- Race condition testing is required (`make race`)\n- Test files follow `*_test.go` naming convention","llms.txt":"# Echo\n\n> 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.\n\nKey facts for LLMs:\n- Import path is `github.com/labstack/echo/v5`\n- Handlers use `*echo.Context` (pointer to struct)\n- `Context` is a concrete struct, not an interface\n- Logging uses `log/slog`\n- Go 1.25+ required\n\n## Quick Reference\n\n- [Echo v5 API Changes](https://github.com/labstack/echo/blob/master/API_CHANGES_V5.md): Breaking changes reference\n- [Echo Official Docs](https://echo.labstack.com): Human-readable documentation site\n- [pkg.go.dev](https://pkg.go.dev/github.com/labstack/echo/v5): API reference on Go package discovery\n\n## Core Patterns\n\n### Server setup\n\n```go\npackage main\n\nimport (\n    \"log/slog\"\n    \"net/http\"\n\n    \"github.com/labstack/echo/v5\"\n    \"github.com/labstack/echo/v5/middleware\"\n)\n\nfunc hello(c *echo.Context) error {\n    return c.String(http.StatusOK, \"Hello, World!\")\n}\n\nfunc main() {\n    e := echo.New()\n    e.Use(middleware.RequestLogger())\n    e.Use(middleware.Recover())\n    e.GET(\"/\", hello)\n    if err := e.Start(\":8080\"); err != nil {\n        slog.Error(\"failed to start server\", \"error\", err)\n    }\n}\n```\n\n### Handler signature\n\n```go\nfunc handler(c *echo.Context) error {\n    return c.JSON(http.StatusOK, map[string]string{\"hello\": \"world\"})\n}\n```\n\n### Route registration\n\n```go\ne := echo.New()\ne.GET(\"/users/:id\", getUser)\ne.POST(\"/users\", createUser)\ne.PUT(\"/users/:id\", updateUser)\ne.DELETE(\"/users/:id\", deleteUser)\n\n// Group routes\ng := e.Group(\"/api/v1\")\ng.GET(\"/posts\", listPosts)\ng.POST(\"/posts\", createPost)\n\n// Group with middleware\nadmin := e.Group(\"/admin\", middleware.BasicAuth(basicAuthValidator))\nadmin.GET(\"/stats\", getStats)\n```\n\n### Generic parameter extraction\n\n```go\n// Type-safe path params\nid, err := echo.PathParam[int](c, \"id\")\nname, err := echo.PathParam[string](c, \"name\")\n\n// Query params with defaults\npage, err := echo.QueryParamOr[int](c, \"page\", 1)\ntags, err := echo.QueryParams[string](c, \"tags\")\n\n// Form values\nval, err := echo.FormValue[string](c, \"field\")\nval2, err := echo.FormValueOr[string](c, \"field\", \"default\")\n\n// Supported types: bool, string, int/int8/.../int64, uint/.../uint64,\n// float32/float64, time.Time, time.Duration\n```\n\n### Request binding\n\n```go\ntype User struct {\n    Name  string `json:\"name\"`\n    Email string `json:\"email\"`\n}\n\nfunc createUser(c *echo.Context) error {\n    u := new(User)\n    if err := c.Bind(u); err != nil {\n        return echo.NewHTTPError(http.StatusBadRequest, \"invalid request body\")\n    }\n    if err := c.Validate(u); err != nil {\n        return err\n    }\n    return c.JSON(http.StatusCreated, u)\n}\n```\n\n### Error handling\n\n```go\n// NewHTTPError takes a string message\nerr := echo.NewHTTPError(http.StatusBadRequest, \"invalid input\")\n\n// Custom error handler\ne.HTTPErrorHandler = func(c *echo.Context, err error) {\n    // custom error handling\n}\n\n// DefaultHTTPErrorHandler is a factory (exposeError bool)\ne.HTTPErrorHandler = echo.DefaultHTTPErrorHandler(true)\n```\n\n### Logging\n\n```go\n// Echo uses the standard library slog\ne.Logger = slog.Default()\n\n// In handlers\nc.Logger().Info(\"request\", \"path\", c.Path(), \"method\", c.Request().Method)\n\n// Custom logger\nlogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))\ne.Logger = logger\n```\n\n### Middleware\n\n```go\n// Built-in middleware\ne.Use(middleware.RequestLogger())\ne.Use(middleware.Recover())\ne.Use(middleware.CORS())\ne.Use(middleware.Gzip())\ne.Use(middleware.BasicAuth(func(c *echo.Context, username, password string) (bool, error) {\n    return username == \"admin\" && password == \"secret\", nil\n}))\ne.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(20)))\n\n// Route-level middleware\ne.GET(\"/sensitive\", handler, middleware.RequestID(), middleware.Logger())\n```\n\n### Response helpers\n\n```go\nc.String(http.StatusOK, \"text\")\nc.JSON(http.StatusOK, data)\nc.JSONPretty(http.StatusOK, data, \"  \")\nc.XML(http.StatusOK, data)\nc.Blob(http.StatusOK, \"image/png\", bytes)\nc.Stream(http.StatusOK, \"text/event-stream\", reader)\nc.File(\"path/to/file\")\nc.FileFS(\"file.txt\", filesystem)\nc.Attachment(\"path/to/file\", \"download.txt\")\nc.Redirect(http.StatusFound, \"/new-path\")\nc.NoContent(http.StatusOK)\nc.HTML(http.StatusOK, \"<h1>Hello</h1>\")\n```\n\n### Static files\n\n```go\ne.Static(\"/assets\", \"public\")\ne.StaticFS(\"/static\", filesystem)\ne.File(\"/robots.txt\", \"robots.txt\")\ne.FileFS(\"file.txt\", filesystem)\n```\n\n### Context store (type-safe)\n\n```go\n// Set and get values on context\nc.Set(\"user\", user)\nval, err := echo.ContextGet[*User](c, \"user\")\ncount, err := echo.ContextGetOr[int](c, \"count\", 0)\n```\n\n### Graceful shutdown with StartConfig\n\n```go\nctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)\ndefer cancel()\n\nsc := echo.StartConfig{\n    Address:        \":8080\",\n    GracefulTimeout: 10 * time.Second,\n}\nif err := sc.Start(ctx, e); err != nil {\n    log.Fatal(err)\n}\n```\n\n## Optional\n\n- [Echo GitHub Discussions](https://github.com/labstack/echo/discussions): Community Q&A and proposals\n- [Echo Roadmap](https://github.com/labstack/echo/blob/master/ROADMAP.md): Future plans\n- [Echo Changelog](https://github.com/labstack/echo/blob/master/CHANGELOG.md): Release history\n- [API_CHANGES_V5.md](https://github.com/labstack/echo/blob/master/API_CHANGES_V5.md): Full v5 breaking changes reference\n","llms-full.txt":"# Echo\n\n> 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.\n\nImport path: `github.com/labstack/echo/v5`\nGo version: 1.25+\nHandler signature: `func(c *echo.Context) error`\n\n## Context\n\n`Context` is a concrete struct passed by pointer to all handlers.\n\n```go\ntype Context struct { ... }\n\n// Handlers receive *echo.Context\nfunc handler(c *echo.Context) error { ... }\n```\n\n### Context methods\n\n```go\nc.Request() *http.Request\nc.SetRequest(r *http.Request)\nc.Response() http.ResponseWriter\nc.SetResponse(rw http.ResponseWriter)\nc.IsTLS() bool\nc.IsWebSocket() bool\nc.Scheme() string\nc.RealIP() string\nc.Path() string\nc.SetPath(path string)\nc.RouteInfo() RouteInfo\nc.Param(name string) string\nc.ParamOr(name, defaultValue string) string\nc.PathValues() PathValues\nc.SetPathValues(pathValues PathValues)\nc.QueryParam(name string) string\nc.QueryParamOr(name, defaultValue string) string\nc.QueryParams() []string\nc.QueryString() string\nc.FormValue(name string) string\nc.FormValueOr(name, defaultValue string) string\nc.FormValues() []string\nc.FormFile(name string) (*multipart.FileHeader, error)\nc.MultipartForm() (*multipart.Form, error)\nc.Cookie(name string) (*http.Cookie, error)\nc.SetCookie(cookie *http.Cookie)\nc.Cookies() []*http.Cookie\nc.Get(key string) any\nc.Set(key string, val any)\nc.Bind(i any) error\nc.Validate(i any) error\nc.Render(code int, name string, data any) error\nc.HTML(code int, html string) error\nc.HTMLBlob(code int, b []byte) error\nc.String(code int, s string) error\nc.JSON(code int, i any) error\nc.JSONPretty(code int, i any, indent string) error\nc.JSONBlob(code int, b []byte) error\nc.JSONP(code int, callback string, i any) error\nc.JSONPBlob(code int, callback string, i any) error\nc.XML(code int, i any) error\nc.XMLPretty(code int, i any, indent string) error\nc.XMLBlob(code int, b []byte) error\nc.Blob(code int, contentType string, b []byte) error\nc.Stream(code int, contentType string, r io.Reader) error\nc.File(file string) error\nc.FileFS(file string, filesystem fs.FS) error\nc.Attachment(file, name string) error\nc.Inline(file, name string) error\nc.NoContent(code int) error\nc.Redirect(code int, url string) error\nc.Logger() *slog.Logger\nc.SetLogger(logger *slog.Logger)\nc.Echo() *Echo\n```\n\n### Context store (type-safe)\n\n```go\nc.Set(\"user\", user)\nval, err := echo.ContextGet[*User](c, \"user\")\ncount, err := echo.ContextGetOr[int](c, \"count\", 0)\n```\n\n## Echo struct\n\n```go\ntype Echo struct {\n    Binder           Binder\n    Filesystem       fs.FS\n    Renderer         Renderer\n    Validator        Validator\n    JSONSerializer   JSONSerializer\n    IPExtractor      IPExtractor\n    OnAddRoute       func(route Route) error\n    HTTPErrorHandler HTTPErrorHandler\n    Logger           *slog.Logger\n}\n```\n\nConstructor: `echo.New() *Echo` or `echo.NewWithConfig(config Config) *Echo`\n\n## Echo methods\n\n### Routing\n\n```go\ne.GET(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.POST(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.PUT(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.ANY(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) Routes\ne.RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.AddRoute(route Route) (RouteInfo, error)\n```\n\n### Middleware\n\n```go\ne.Use(middleware ...MiddlewareFunc)\ne.Pre(middleware ...MiddlewareFunc)\ne.Middlewares() []MiddlewareFunc\ne.PreMiddlewares() []MiddlewareFunc\n```\n\n### Groups\n\n```go\ne.Group(prefix string, m ...MiddlewareFunc) *Group\n```\n\n### Static files\n\n```go\ne.Static(pathPrefix, fsRoot string, m ...MiddlewareFunc) RouteInfo\ne.StaticFS(pathPrefix string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\ne.File(path, file string, m ...MiddlewareFunc) RouteInfo\ne.FileFS(file string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\n```\n\n### Server\n\n```go\ne.Start(address string) error\ne.StartTLS(address string, certFile, keyFile any) error\n```\n\n```go\ntype StartConfig struct {\n    Address          string\n    HideBanner       bool\n    HidePort         bool\n    CertFilesystem   fs.FS\n    TLSConfig        *tls.Config\n    ListenerNetwork  string\n    ListenerAddrFunc func(addr net.Addr)\n    GracefulTimeout  time.Duration\n    OnShutdownError  func(err error)\n    BeforeServeFunc  func(s *http.Server) error\n}\n\n// StartConfig.Start and StartConfig.StartTLS for graceful shutdown\nsc := echo.StartConfig{Address: \":8080\", GracefulTimeout: 10 * time.Second}\nsc.Start(ctx, e)\n```\n\n## Error handling\n\n```go\n// HTTPError with string message\nerr := echo.NewHTTPError(http.StatusBadRequest, \"invalid input\")\n\n// Custom error handler (params: context pointer, then error)\ne.HTTPErrorHandler = func(c *echo.Context, err error) {\n    // custom error handling\n}\n\n// Default error handler factory (exposeError bool)\ne.HTTPErrorHandler = echo.DefaultHTTPErrorHandler(true)\n```\n\n```go\necho.ErrBadRequest\necho.ErrValidatorNotRegistered\necho.ErrInvalidKeyType\necho.ErrNonExistentKey\n```\n\n## Logging\n\nEcho uses the standard library `log/slog` package.\n\n```go\ne.Logger = slog.Default()\nc.Logger().Info(\"message\", \"key\", \"value\")\n\n// Custom logger\nlogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))\ne.Logger = logger\n```\n\n## Router\n\n```go\n// Router interface with DefaultRouter implementation\nfunc NewRouter(config RouterConfig) *DefaultRouter\nfunc (e *Echo) Router() Router\n\n// Thread-safe routing\nfunc NewConcurrentRouter(r Router) Router\n```\n\n## RouteInfo\n\n```go\ntype RouteInfo struct {\n    Name       string\n    Method     string\n    Path       string\n    Parameters []string\n}\n\n// Routes type with filtering methods\ntype Routes []RouteInfo\n// Methods: FilterByMethod, FilterByName, FilterByPath, FindByMethodPath, Reverse\n```\n\n## PathValues\n\n```go\ntype PathValue struct {\n    Name  string\n    Value string\n}\ntype PathValues []PathValue\n\nfunc (p PathValues) Get(name string) (string, bool)\nfunc (p PathValues) GetOr(name string, defaultValue string) string\n```\n\n## Generic helper functions\n\n```go\necho.PathParam[T](c *Context, name string, opts ...any) (T, error)\necho.PathParamOr[T](c *Context, name string, defaultValue T, opts ...any) (T, error)\necho.QueryParam[T](c *Context, key string, opts ...any) (T, error)\necho.QueryParamOr[T](c *Context, key string, defaultValue T, opts ...any) (T, error)\necho.QueryParams[T](c *Context, key string, opts ...any) ([]T, error)\necho.QueryParamsOr[T](c *Context, key string, defaultValue []T, opts ...any) ([]T, error)\necho.FormValue[T](c *Context, key string, opts ...any) (T, error)\necho.FormValueOr[T](c *Context, key string, defaultValue T, opts ...any) (T, error)\necho.FormValues[T](c *Context, key string, opts ...any) ([]T, error)\necho.FormValuesOr[T](c *Context, key string, defaultValue []T, opts ...any) ([]T, error)\necho.ParseValue[T](value string, opts ...any) (T, error)\necho.ParseValueOr[T](value string, defaultValue T, opts ...any) (T, error)\necho.ParseValues[T](values []string, opts ...any) ([]T, error)\necho.ParseValuesOr[T](values []string, defaultValue []T, opts ...any) ([]T, error)\necho.ContextGet[T](c *Context, key string) (T, error)\necho.ContextGetOr[T](c *Context, key string, defaultValue T) (T, error)\n```\n\nSupported 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\n\n## Binding functions\n\n```go\necho.BindBody(c *Context, target any) error\necho.BindHeaders(c *Context, target any) error\necho.BindPathValues(c *Context, target any) error\necho.BindQueryParams(c *Context, target any) error\n```\n\n### Binder helpers\n\n```go\necho.PathValuesBinder(c *Context) *ValueBinder\necho.QueryParamsBinder(c *Context) *ValueBinder\necho.FormFieldBinder(c *Context) *ValueBinder\n```\n\n## Group methods\n\n```go\ng.Use(m ...MiddlewareFunc)\ng.Group(prefix string, m ...MiddlewareFunc) *Group\ng.GET(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.POST(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.PUT(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.ANY(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) Routes\ng.Static(pathPrefix, fsRoot string, m ...MiddlewareFunc) RouteInfo\ng.StaticFS(pathPrefix string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\ng.File(path, file string, m ...MiddlewareFunc) RouteInfo\ng.FileFS(file string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\n```\n\n## Built-in middleware\n\n| Middleware | Description |\n|---|---|\n| BasicAuth | HTTP basic authentication |\n| BodyDump | Dumps request and response bodies |\n| BodyLimit | Limits request body size |\n| Compress | Gzip response compression |\n| ContextTimeout | Request context timeout |\n| CORS | Cross-origin resource sharing |\n| CSRF | Cross-site request forgery protection |\n| Decompress | Gzip request decompression |\n| KeyAuth | Key-based authentication |\n| MethodOverride | HTTP method override |\n| Proxy | Reverse proxy |\n| RateLimiter | Rate limiting |\n| Recover | Panic recovery |\n| Redirect | HTTP redirect |\n| RequestID | Request ID header |\n| RequestLogger | Structured request logging |\n| Rewrite | URL rewriting |\n| Secure | Security headers |\n| Slash | Trailing slash handling |\n| Static | Static file serving |\n\n## Wrapping stdlib handlers\n\n```go\necho.WrapHandler(h http.Handler) HandlerFunc\necho.WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc\n```\n\n## Virtual host\n\n```go\necho.NewVirtualHostHandler(vhosts map[string]*Echo) *Echo\n```\n\n## echotest package\n\n```go\nimport \"github.com/labstack/echo/v5/echotest\"\n\nechotest.LoadBytes(t *testing.T, name string, opts ...loadBytesOpts) []byte\nechotest.TrimNewlineEnd(bytes []byte) []byte\nechotest.ContextConfig{...}\nechotest.MultipartForm{...}\nechotest.MultipartFormFile{...}\n```\n\n## Complete server example\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"log/slog\"\n    \"net/http\"\n    \"os\"\n    \"os/signal\"\n    \"syscall\"\n    \"time\"\n\n    \"github.com/labstack/echo/v5\"\n    \"github.com/labstack/echo/v5/middleware\"\n)\n\nfunc main() {\n    e := echo.New()\n    e.Use(middleware.RequestLogger())\n    e.Use(middleware.Recover())\n\n    e.GET(\"/\", func(c *echo.Context) error {\n        return c.String(http.StatusOK, \"Hello, World!\")\n    })\n\n    e.GET(\"/users/:id\", func(c *echo.Context) error {\n        id, err := echo.PathParam[int](c, \"id\")\n        if err != nil {\n            return echo.NewHTTPError(http.StatusBadRequest, \"invalid user id\")\n        }\n        return c.JSON(http.StatusOK, map[string]int{\"id\": id})\n    })\n\n    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)\n    defer cancel()\n\n    sc := echo.StartConfig{\n        Address:        \":8080\",\n        GracefulTimeout: 10 * time.Second,\n    }\n    if err := sc.Start(ctx, e); err != nil {\n        slog.Error(\"server error\", \"error\", err)\n    }\n}\n```\n"},"files":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## About This Project\n\nEcho is a high performance, minimalist Go web framework. This is the main repository for Echo v5, which is available as a Go module at `github.com/labstack/echo/v5`.\n\n## Development Commands\n\nThe project uses a Makefile for common development tasks:\n\n- `make check` - Run linting, vetting, and race condition tests (default target)\n- `make init` - Install required linting tools (golint, staticcheck)\n- `make lint` - Run staticcheck and golint\n- `make vet` - Run go vet\n- `make test` - Run short tests\n- `make race` - Run tests with race detector\n- `make benchmark` - Run benchmarks\n\nExample commands for development:\n```bash\n# Setup development environment\nmake init\n\n# Run all checks (lint, vet, race)\nmake check\n\n# Run specific tests\ngo test ./middleware/...\ngo test -race ./...\n\n# Run benchmarks\nmake benchmark\n```\n\n## Code Architecture\n\n### Core Components\n\n**Echo Instance (`echo.go`)**\n- The `Echo` struct is the top-level framework instance\n- Contains router, middleware stacks, and server configuration\n- Not goroutine-safe for mutations after server start\n\n**Context (`context.go`)**\n- The `Context` struct represents HTTP request/response context\n- Provides methods for request/response handling, path parameters, data binding\n- Core abstraction for request processing\n\n**Router (`router.go`)**\n- Radix tree-based HTTP router with smart route prioritization\n- Supports static routes, parameterized routes (`/users/:id`), and wildcard routes (`/static/*`)\n- Each HTTP method has its own routing tree\n\n**Middleware (`middleware/`)**\n- Extensive middleware system with 50+ built-in middlewares\n- Middleware can be applied at Echo, Group, or individual route level\n- Common middleware: Logger, Recover, CORS, JWT, Rate Limiting, etc.\n\n### Key Patterns\n\n**Middleware Chain**\n- Pre-middleware runs before routing\n- Regular middleware runs after routing but before handlers\n- Middleware functions have signature `func(next echo.HandlerFunc) echo.HandlerFunc`\n\n**Route Groups**\n- Routes can be grouped with common prefixes and middleware\n- Groups support nested sub-groups\n- Defined in `group.go`\n\n**Data Binding**\n- Automatic binding of request data (JSON, XML, form) to Go structs\n- Implemented in `binder.go` with support for custom binders\n\n**Error Handling**\n- Centralized error handling via `HTTPErrorHandler`\n- Automatic panic recovery with stack traces\n\n## File Organization\n\n- Root directory: Core Echo functionality (echo.go, context.go, router.go, etc.)\n- `middleware/`: All built-in middleware implementations\n- `_test/`: Test fixtures and utilities\n- `_fixture/`: Test data files\n\n## Code Style\n\n- Go code uses tabs for indentation (per .editorconfig)\n- Follows standard Go conventions and formatting\n- Uses gofmt, golint, and staticcheck for code quality\n\n## Testing\n\n- Standard Go testing with `testing` package\n- Tests include unit tests, integration tests, and benchmarks\n- Race condition testing is required (`make race`)\n- Test files follow `*_test.go` naming convention","llms.txt":"# Echo\n\n> 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.\n\nKey facts for LLMs:\n- Import path is `github.com/labstack/echo/v5`\n- Handlers use `*echo.Context` (pointer to struct)\n- `Context` is a concrete struct, not an interface\n- Logging uses `log/slog`\n- Go 1.25+ required\n\n## Quick Reference\n\n- [Echo v5 API Changes](https://github.com/labstack/echo/blob/master/API_CHANGES_V5.md): Breaking changes reference\n- [Echo Official Docs](https://echo.labstack.com): Human-readable documentation site\n- [pkg.go.dev](https://pkg.go.dev/github.com/labstack/echo/v5): API reference on Go package discovery\n\n## Core Patterns\n\n### Server setup\n\n```go\npackage main\n\nimport (\n    \"log/slog\"\n    \"net/http\"\n\n    \"github.com/labstack/echo/v5\"\n    \"github.com/labstack/echo/v5/middleware\"\n)\n\nfunc hello(c *echo.Context) error {\n    return c.String(http.StatusOK, \"Hello, World!\")\n}\n\nfunc main() {\n    e := echo.New()\n    e.Use(middleware.RequestLogger())\n    e.Use(middleware.Recover())\n    e.GET(\"/\", hello)\n    if err := e.Start(\":8080\"); err != nil {\n        slog.Error(\"failed to start server\", \"error\", err)\n    }\n}\n```\n\n### Handler signature\n\n```go\nfunc handler(c *echo.Context) error {\n    return c.JSON(http.StatusOK, map[string]string{\"hello\": \"world\"})\n}\n```\n\n### Route registration\n\n```go\ne := echo.New()\ne.GET(\"/users/:id\", getUser)\ne.POST(\"/users\", createUser)\ne.PUT(\"/users/:id\", updateUser)\ne.DELETE(\"/users/:id\", deleteUser)\n\n// Group routes\ng := e.Group(\"/api/v1\")\ng.GET(\"/posts\", listPosts)\ng.POST(\"/posts\", createPost)\n\n// Group with middleware\nadmin := e.Group(\"/admin\", middleware.BasicAuth(basicAuthValidator))\nadmin.GET(\"/stats\", getStats)\n```\n\n### Generic parameter extraction\n\n```go\n// Type-safe path params\nid, err := echo.PathParam[int](c, \"id\")\nname, err := echo.PathParam[string](c, \"name\")\n\n// Query params with defaults\npage, err := echo.QueryParamOr[int](c, \"page\", 1)\ntags, err := echo.QueryParams[string](c, \"tags\")\n\n// Form values\nval, err := echo.FormValue[string](c, \"field\")\nval2, err := echo.FormValueOr[string](c, \"field\", \"default\")\n\n// Supported types: bool, string, int/int8/.../int64, uint/.../uint64,\n// float32/float64, time.Time, time.Duration\n```\n\n### Request binding\n\n```go\ntype User struct {\n    Name  string `json:\"name\"`\n    Email string `json:\"email\"`\n}\n\nfunc createUser(c *echo.Context) error {\n    u := new(User)\n    if err := c.Bind(u); err != nil {\n        return echo.NewHTTPError(http.StatusBadRequest, \"invalid request body\")\n    }\n    if err := c.Validate(u); err != nil {\n        return err\n    }\n    return c.JSON(http.StatusCreated, u)\n}\n```\n\n### Error handling\n\n```go\n// NewHTTPError takes a string message\nerr := echo.NewHTTPError(http.StatusBadRequest, \"invalid input\")\n\n// Custom error handler\ne.HTTPErrorHandler = func(c *echo.Context, err error) {\n    // custom error handling\n}\n\n// DefaultHTTPErrorHandler is a factory (exposeError bool)\ne.HTTPErrorHandler = echo.DefaultHTTPErrorHandler(true)\n```\n\n### Logging\n\n```go\n// Echo uses the standard library slog\ne.Logger = slog.Default()\n\n// In handlers\nc.Logger().Info(\"request\", \"path\", c.Path(), \"method\", c.Request().Method)\n\n// Custom logger\nlogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))\ne.Logger = logger\n```\n\n### Middleware\n\n```go\n// Built-in middleware\ne.Use(middleware.RequestLogger())\ne.Use(middleware.Recover())\ne.Use(middleware.CORS())\ne.Use(middleware.Gzip())\ne.Use(middleware.BasicAuth(func(c *echo.Context, username, password string) (bool, error) {\n    return username == \"admin\" && password == \"secret\", nil\n}))\ne.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(20)))\n\n// Route-level middleware\ne.GET(\"/sensitive\", handler, middleware.RequestID(), middleware.Logger())\n```\n\n### Response helpers\n\n```go\nc.String(http.StatusOK, \"text\")\nc.JSON(http.StatusOK, data)\nc.JSONPretty(http.StatusOK, data, \"  \")\nc.XML(http.StatusOK, data)\nc.Blob(http.StatusOK, \"image/png\", bytes)\nc.Stream(http.StatusOK, \"text/event-stream\", reader)\nc.File(\"path/to/file\")\nc.FileFS(\"file.txt\", filesystem)\nc.Attachment(\"path/to/file\", \"download.txt\")\nc.Redirect(http.StatusFound, \"/new-path\")\nc.NoContent(http.StatusOK)\nc.HTML(http.StatusOK, \"<h1>Hello</h1>\")\n```\n\n### Static files\n\n```go\ne.Static(\"/assets\", \"public\")\ne.StaticFS(\"/static\", filesystem)\ne.File(\"/robots.txt\", \"robots.txt\")\ne.FileFS(\"file.txt\", filesystem)\n```\n\n### Context store (type-safe)\n\n```go\n// Set and get values on context\nc.Set(\"user\", user)\nval, err := echo.ContextGet[*User](c, \"user\")\ncount, err := echo.ContextGetOr[int](c, \"count\", 0)\n```\n\n### Graceful shutdown with StartConfig\n\n```go\nctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)\ndefer cancel()\n\nsc := echo.StartConfig{\n    Address:        \":8080\",\n    GracefulTimeout: 10 * time.Second,\n}\nif err := sc.Start(ctx, e); err != nil {\n    log.Fatal(err)\n}\n```\n\n## Optional\n\n- [Echo GitHub Discussions](https://github.com/labstack/echo/discussions): Community Q&A and proposals\n- [Echo Roadmap](https://github.com/labstack/echo/blob/master/ROADMAP.md): Future plans\n- [Echo Changelog](https://github.com/labstack/echo/blob/master/CHANGELOG.md): Release history\n- [API_CHANGES_V5.md](https://github.com/labstack/echo/blob/master/API_CHANGES_V5.md): Full v5 breaking changes reference\n","llms-full.txt":"# Echo\n\n> 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.\n\nImport path: `github.com/labstack/echo/v5`\nGo version: 1.25+\nHandler signature: `func(c *echo.Context) error`\n\n## Context\n\n`Context` is a concrete struct passed by pointer to all handlers.\n\n```go\ntype Context struct { ... }\n\n// Handlers receive *echo.Context\nfunc handler(c *echo.Context) error { ... }\n```\n\n### Context methods\n\n```go\nc.Request() *http.Request\nc.SetRequest(r *http.Request)\nc.Response() http.ResponseWriter\nc.SetResponse(rw http.ResponseWriter)\nc.IsTLS() bool\nc.IsWebSocket() bool\nc.Scheme() string\nc.RealIP() string\nc.Path() string\nc.SetPath(path string)\nc.RouteInfo() RouteInfo\nc.Param(name string) string\nc.ParamOr(name, defaultValue string) string\nc.PathValues() PathValues\nc.SetPathValues(pathValues PathValues)\nc.QueryParam(name string) string\nc.QueryParamOr(name, defaultValue string) string\nc.QueryParams() []string\nc.QueryString() string\nc.FormValue(name string) string\nc.FormValueOr(name, defaultValue string) string\nc.FormValues() []string\nc.FormFile(name string) (*multipart.FileHeader, error)\nc.MultipartForm() (*multipart.Form, error)\nc.Cookie(name string) (*http.Cookie, error)\nc.SetCookie(cookie *http.Cookie)\nc.Cookies() []*http.Cookie\nc.Get(key string) any\nc.Set(key string, val any)\nc.Bind(i any) error\nc.Validate(i any) error\nc.Render(code int, name string, data any) error\nc.HTML(code int, html string) error\nc.HTMLBlob(code int, b []byte) error\nc.String(code int, s string) error\nc.JSON(code int, i any) error\nc.JSONPretty(code int, i any, indent string) error\nc.JSONBlob(code int, b []byte) error\nc.JSONP(code int, callback string, i any) error\nc.JSONPBlob(code int, callback string, i any) error\nc.XML(code int, i any) error\nc.XMLPretty(code int, i any, indent string) error\nc.XMLBlob(code int, b []byte) error\nc.Blob(code int, contentType string, b []byte) error\nc.Stream(code int, contentType string, r io.Reader) error\nc.File(file string) error\nc.FileFS(file string, filesystem fs.FS) error\nc.Attachment(file, name string) error\nc.Inline(file, name string) error\nc.NoContent(code int) error\nc.Redirect(code int, url string) error\nc.Logger() *slog.Logger\nc.SetLogger(logger *slog.Logger)\nc.Echo() *Echo\n```\n\n### Context store (type-safe)\n\n```go\nc.Set(\"user\", user)\nval, err := echo.ContextGet[*User](c, \"user\")\ncount, err := echo.ContextGetOr[int](c, \"count\", 0)\n```\n\n## Echo struct\n\n```go\ntype Echo struct {\n    Binder           Binder\n    Filesystem       fs.FS\n    Renderer         Renderer\n    Validator        Validator\n    JSONSerializer   JSONSerializer\n    IPExtractor      IPExtractor\n    OnAddRoute       func(route Route) error\n    HTTPErrorHandler HTTPErrorHandler\n    Logger           *slog.Logger\n}\n```\n\nConstructor: `echo.New() *Echo` or `echo.NewWithConfig(config Config) *Echo`\n\n## Echo methods\n\n### Routing\n\n```go\ne.GET(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.POST(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.PUT(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.ANY(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) Routes\ne.RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.AddRoute(route Route) (RouteInfo, error)\n```\n\n### Middleware\n\n```go\ne.Use(middleware ...MiddlewareFunc)\ne.Pre(middleware ...MiddlewareFunc)\ne.Middlewares() []MiddlewareFunc\ne.PreMiddlewares() []MiddlewareFunc\n```\n\n### Groups\n\n```go\ne.Group(prefix string, m ...MiddlewareFunc) *Group\n```\n\n### Static files\n\n```go\ne.Static(pathPrefix, fsRoot string, m ...MiddlewareFunc) RouteInfo\ne.StaticFS(pathPrefix string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\ne.File(path, file string, m ...MiddlewareFunc) RouteInfo\ne.FileFS(file string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\n```\n\n### Server\n\n```go\ne.Start(address string) error\ne.StartTLS(address string, certFile, keyFile any) error\n```\n\n```go\ntype StartConfig struct {\n    Address          string\n    HideBanner       bool\n    HidePort         bool\n    CertFilesystem   fs.FS\n    TLSConfig        *tls.Config\n    ListenerNetwork  string\n    ListenerAddrFunc func(addr net.Addr)\n    GracefulTimeout  time.Duration\n    OnShutdownError  func(err error)\n    BeforeServeFunc  func(s *http.Server) error\n}\n\n// StartConfig.Start and StartConfig.StartTLS for graceful shutdown\nsc := echo.StartConfig{Address: \":8080\", GracefulTimeout: 10 * time.Second}\nsc.Start(ctx, e)\n```\n\n## Error handling\n\n```go\n// HTTPError with string message\nerr := echo.NewHTTPError(http.StatusBadRequest, \"invalid input\")\n\n// Custom error handler (params: context pointer, then error)\ne.HTTPErrorHandler = func(c *echo.Context, err error) {\n    // custom error handling\n}\n\n// Default error handler factory (exposeError bool)\ne.HTTPErrorHandler = echo.DefaultHTTPErrorHandler(true)\n```\n\n```go\necho.ErrBadRequest\necho.ErrValidatorNotRegistered\necho.ErrInvalidKeyType\necho.ErrNonExistentKey\n```\n\n## Logging\n\nEcho uses the standard library `log/slog` package.\n\n```go\ne.Logger = slog.Default()\nc.Logger().Info(\"message\", \"key\", \"value\")\n\n// Custom logger\nlogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))\ne.Logger = logger\n```\n\n## Router\n\n```go\n// Router interface with DefaultRouter implementation\nfunc NewRouter(config RouterConfig) *DefaultRouter\nfunc (e *Echo) Router() Router\n\n// Thread-safe routing\nfunc NewConcurrentRouter(r Router) Router\n```\n\n## RouteInfo\n\n```go\ntype RouteInfo struct {\n    Name       string\n    Method     string\n    Path       string\n    Parameters []string\n}\n\n// Routes type with filtering methods\ntype Routes []RouteInfo\n// Methods: FilterByMethod, FilterByName, FilterByPath, FindByMethodPath, Reverse\n```\n\n## PathValues\n\n```go\ntype PathValue struct {\n    Name  string\n    Value string\n}\ntype PathValues []PathValue\n\nfunc (p PathValues) Get(name string) (string, bool)\nfunc (p PathValues) GetOr(name string, defaultValue string) string\n```\n\n## Generic helper functions\n\n```go\necho.PathParam[T](c *Context, name string, opts ...any) (T, error)\necho.PathParamOr[T](c *Context, name string, defaultValue T, opts ...any) (T, error)\necho.QueryParam[T](c *Context, key string, opts ...any) (T, error)\necho.QueryParamOr[T](c *Context, key string, defaultValue T, opts ...any) (T, error)\necho.QueryParams[T](c *Context, key string, opts ...any) ([]T, error)\necho.QueryParamsOr[T](c *Context, key string, defaultValue []T, opts ...any) ([]T, error)\necho.FormValue[T](c *Context, key string, opts ...any) (T, error)\necho.FormValueOr[T](c *Context, key string, defaultValue T, opts ...any) (T, error)\necho.FormValues[T](c *Context, key string, opts ...any) ([]T, error)\necho.FormValuesOr[T](c *Context, key string, defaultValue []T, opts ...any) ([]T, error)\necho.ParseValue[T](value string, opts ...any) (T, error)\necho.ParseValueOr[T](value string, defaultValue T, opts ...any) (T, error)\necho.ParseValues[T](values []string, opts ...any) ([]T, error)\necho.ParseValuesOr[T](values []string, defaultValue []T, opts ...any) ([]T, error)\necho.ContextGet[T](c *Context, key string) (T, error)\necho.ContextGetOr[T](c *Context, key string, defaultValue T) (T, error)\n```\n\nSupported 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\n\n## Binding functions\n\n```go\necho.BindBody(c *Context, target any) error\necho.BindHeaders(c *Context, target any) error\necho.BindPathValues(c *Context, target any) error\necho.BindQueryParams(c *Context, target any) error\n```\n\n### Binder helpers\n\n```go\necho.PathValuesBinder(c *Context) *ValueBinder\necho.QueryParamsBinder(c *Context) *ValueBinder\necho.FormFieldBinder(c *Context) *ValueBinder\n```\n\n## Group methods\n\n```go\ng.Use(m ...MiddlewareFunc)\ng.Group(prefix string, m ...MiddlewareFunc) *Group\ng.GET(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.POST(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.PUT(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.ANY(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) Routes\ng.Static(pathPrefix, fsRoot string, m ...MiddlewareFunc) RouteInfo\ng.StaticFS(pathPrefix string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\ng.File(path, file string, m ...MiddlewareFunc) RouteInfo\ng.FileFS(file string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\n```\n\n## Built-in middleware\n\n| Middleware | Description |\n|---|---|\n| BasicAuth | HTTP basic authentication |\n| BodyDump | Dumps request and response bodies |\n| BodyLimit | Limits request body size |\n| Compress | Gzip response compression |\n| ContextTimeout | Request context timeout |\n| CORS | Cross-origin resource sharing |\n| CSRF | Cross-site request forgery protection |\n| Decompress | Gzip request decompression |\n| KeyAuth | Key-based authentication |\n| MethodOverride | HTTP method override |\n| Proxy | Reverse proxy |\n| RateLimiter | Rate limiting |\n| Recover | Panic recovery |\n| Redirect | HTTP redirect |\n| RequestID | Request ID header |\n| RequestLogger | Structured request logging |\n| Rewrite | URL rewriting |\n| Secure | Security headers |\n| Slash | Trailing slash handling |\n| Static | Static file serving |\n\n## Wrapping stdlib handlers\n\n```go\necho.WrapHandler(h http.Handler) HandlerFunc\necho.WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc\n```\n\n## Virtual host\n\n```go\necho.NewVirtualHostHandler(vhosts map[string]*Echo) *Echo\n```\n\n## echotest package\n\n```go\nimport \"github.com/labstack/echo/v5/echotest\"\n\nechotest.LoadBytes(t *testing.T, name string, opts ...loadBytesOpts) []byte\nechotest.TrimNewlineEnd(bytes []byte) []byte\nechotest.ContextConfig{...}\nechotest.MultipartForm{...}\nechotest.MultipartFormFile{...}\n```\n\n## Complete server example\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"log/slog\"\n    \"net/http\"\n    \"os\"\n    \"os/signal\"\n    \"syscall\"\n    \"time\"\n\n    \"github.com/labstack/echo/v5\"\n    \"github.com/labstack/echo/v5/middleware\"\n)\n\nfunc main() {\n    e := echo.New()\n    e.Use(middleware.RequestLogger())\n    e.Use(middleware.Recover())\n\n    e.GET(\"/\", func(c *echo.Context) error {\n        return c.String(http.StatusOK, \"Hello, World!\")\n    })\n\n    e.GET(\"/users/:id\", func(c *echo.Context) error {\n        id, err := echo.PathParam[int](c, \"id\")\n        if err != nil {\n            return echo.NewHTTPError(http.StatusBadRequest, \"invalid user id\")\n        }\n        return c.JSON(http.StatusOK, map[string]int{\"id\": id})\n    })\n\n    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)\n    defer cancel()\n\n    sc := echo.StartConfig{\n        Address:        \":8080\",\n        GracefulTimeout: 10 * time.Second,\n    }\n    if err := sc.Start(ctx, e); err != nil {\n        slog.Error(\"server error\", \"error\", err)\n    }\n}\n```\n"},"items":[{"name":"llms-full.txt","path":"llms-full.txt","rawUrl":"https://raw.githubusercontent.com/labstack/echo/HEAD/llms-full.txt","title":"LLM Full Documentation Context","category":"root-instruction","format":"text","content":"# Echo\n\n> 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.\n\nImport path: `github.com/labstack/echo/v5`\nGo version: 1.25+\nHandler signature: `func(c *echo.Context) error`\n\n## Context\n\n`Context` is a concrete struct passed by pointer to all handlers.\n\n```go\ntype Context struct { ... }\n\n// Handlers receive *echo.Context\nfunc handler(c *echo.Context) error { ... }\n```\n\n### Context methods\n\n```go\nc.Request() *http.Request\nc.SetRequest(r *http.Request)\nc.Response() http.ResponseWriter\nc.SetResponse(rw http.ResponseWriter)\nc.IsTLS() bool\nc.IsWebSocket() bool\nc.Scheme() string\nc.RealIP() string\nc.Path() string\nc.SetPath(path string)\nc.RouteInfo() RouteInfo\nc.Param(name string) string\nc.ParamOr(name, defaultValue string) string\nc.PathValues() PathValues\nc.SetPathValues(pathValues PathValues)\nc.QueryParam(name string) string\nc.QueryParamOr(name, defaultValue string) string\nc.QueryParams() []string\nc.QueryString() string\nc.FormValue(name string) string\nc.FormValueOr(name, defaultValue string) string\nc.FormValues() []string\nc.FormFile(name string) (*multipart.FileHeader, error)\nc.MultipartForm() (*multipart.Form, error)\nc.Cookie(name string) (*http.Cookie, error)\nc.SetCookie(cookie *http.Cookie)\nc.Cookies() []*http.Cookie\nc.Get(key string) any\nc.Set(key string, val any)\nc.Bind(i any) error\nc.Validate(i any) error\nc.Render(code int, name string, data any) error\nc.HTML(code int, html string) error\nc.HTMLBlob(code int, b []byte) error\nc.String(code int, s string) error\nc.JSON(code int, i any) error\nc.JSONPretty(code int, i any, indent string) error\nc.JSONBlob(code int, b []byte) error\nc.JSONP(code int, callback string, i any) error\nc.JSONPBlob(code int, callback string, i any) error\nc.XML(code int, i any) error\nc.XMLPretty(code int, i any, indent string) error\nc.XMLBlob(code int, b []byte) error\nc.Blob(code int, contentType string, b []byte) error\nc.Stream(code int, contentType string, r io.Reader) error\nc.File(file string) error\nc.FileFS(file string, filesystem fs.FS) error\nc.Attachment(file, name string) error\nc.Inline(file, name string) error\nc.NoContent(code int) error\nc.Redirect(code int, url string) error\nc.Logger() *slog.Logger\nc.SetLogger(logger *slog.Logger)\nc.Echo() *Echo\n```\n\n### Context store (type-safe)\n\n```go\nc.Set(\"user\", user)\nval, err := echo.ContextGet[*User](c, \"user\")\ncount, err := echo.ContextGetOr[int](c, \"count\", 0)\n```\n\n## Echo struct\n\n```go\ntype Echo struct {\n    Binder           Binder\n    Filesystem       fs.FS\n    Renderer         Renderer\n    Validator        Validator\n    JSONSerializer   JSONSerializer\n    IPExtractor      IPExtractor\n    OnAddRoute       func(route Route) error\n    HTTPErrorHandler HTTPErrorHandler\n    Logger           *slog.Logger\n}\n```\n\nConstructor: `echo.New() *Echo` or `echo.NewWithConfig(config Config) *Echo`\n\n## Echo methods\n\n### Routing\n\n```go\ne.GET(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.POST(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.PUT(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.ANY(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) Routes\ne.RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.AddRoute(route Route) (RouteInfo, error)\n```\n\n### Middleware\n\n```go\ne.Use(middleware ...MiddlewareFunc)\ne.Pre(middleware ...MiddlewareFunc)\ne.Middlewares() []MiddlewareFunc\ne.PreMiddlewares() []MiddlewareFunc\n```\n\n### Groups\n\n```go\ne.Group(prefix string, m ...MiddlewareFunc) *Group\n```\n\n### Static files\n\n```go\ne.Static(pathPrefix, fsRoot string, m ...MiddlewareFunc) RouteInfo\ne.StaticFS(pathPrefix string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\ne.File(path, file string, m ...MiddlewareFunc) RouteInfo\ne.FileFS(file string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\n```\n\n### Server\n\n```go\ne.Start(address string) error\ne.StartTLS(address string, certFile, keyFile any) error\n```\n\n```go\ntype StartConfig struct {\n    Address          string\n    HideBanner       bool\n    HidePort         bool\n    CertFilesystem   fs.FS\n    TLSConfig        *tls.Config\n    ListenerNetwork  string\n    ListenerAddrFunc func(addr net.Addr)\n    GracefulTimeout  time.Duration\n    OnShutdownError  func(err error)\n    BeforeServeFunc  func(s *http.Server) error\n}\n\n// StartConfig.Start and StartConfig.StartTLS for graceful shutdown\nsc := echo.StartConfig{Address: \":8080\", GracefulTimeout: 10 * time.Second}\nsc.Start(ctx, e)\n```\n\n## Error handling\n\n```go\n// HTTPError with string message\nerr := echo.NewHTTPError(http.StatusBadRequest, \"invalid input\")\n\n// Custom error handler (params: context pointer, then error)\ne.HTTPErrorHandler = func(c *echo.Context, err error) {\n    // custom error handling\n}\n\n// Default error handler factory (exposeError bool)\ne.HTTPErrorHandler = echo.DefaultHTTPErrorHandler(true)\n```\n\n```go\necho.ErrBadRequest\necho.ErrValidatorNotRegistered\necho.ErrInvalidKeyType\necho.ErrNonExistentKey\n```\n\n## Logging\n\nEcho uses the standard library `log/slog` package.\n\n```go\ne.Logger = slog.Default()\nc.Logger().Info(\"message\", \"key\", \"value\")\n\n// Custom logger\nlogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))\ne.Logger = logger\n```\n\n## Router\n\n```go\n// Router interface with DefaultRouter implementation\nfunc NewRouter(config RouterConfig) *DefaultRouter\nfunc (e *Echo) Router() Router\n\n// Thread-safe routing\nfunc NewConcurrentRouter(r Router) Router\n```\n\n## RouteInfo\n\n```go\ntype RouteInfo struct {\n    Name       string\n    Method     string\n    Path       string\n    Parameters []string\n}\n\n// Routes type with filtering methods\ntype Routes []RouteInfo\n// Methods: FilterByMethod, FilterByName, FilterByPath, FindByMethodPath, Reverse\n```\n\n## PathValues\n\n```go\ntype PathValue struct {\n    Name  string\n    Value string\n}\ntype PathValues []PathValue\n\nfunc (p PathValues) Get(name string) (string, bool)\nfunc (p PathValues) GetOr(name string, defaultValue string) string\n```\n\n## Generic helper functions\n\n```go\necho.PathParam[T](c *Context, name string, opts ...any) (T, error)\necho.PathParamOr[T](c *Context, name string, defaultValue T, opts ...any) (T, error)\necho.QueryParam[T](c *Context, key string, opts ...any) (T, error)\necho.QueryParamOr[T](c *Context, key string, defaultValue T, opts ...any) (T, error)\necho.QueryParams[T](c *Context, key string, opts ...any) ([]T, error)\necho.QueryParamsOr[T](c *Context, key string, defaultValue []T, opts ...any) ([]T, error)\necho.FormValue[T](c *Context, key string, opts ...any) (T, error)\necho.FormValueOr[T](c *Context, key string, defaultValue T, opts ...any) (T, error)\necho.FormValues[T](c *Context, key string, opts ...any) ([]T, error)\necho.FormValuesOr[T](c *Context, key string, defaultValue []T, opts ...any) ([]T, error)\necho.ParseValue[T](value string, opts ...any) (T, error)\necho.ParseValueOr[T](value string, defaultValue T, opts ...any) (T, error)\necho.ParseValues[T](values []string, opts ...any) ([]T, error)\necho.ParseValuesOr[T](values []string, defaultValue []T, opts ...any) ([]T, error)\necho.ContextGet[T](c *Context, key string) (T, error)\necho.ContextGetOr[T](c *Context, key string, defaultValue T) (T, error)\n```\n\nSupported 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\n\n## Binding functions\n\n```go\necho.BindBody(c *Context, target any) error\necho.BindHeaders(c *Context, target any) error\necho.BindPathValues(c *Context, target any) error\necho.BindQueryParams(c *Context, target any) error\n```\n\n### Binder helpers\n\n```go\necho.PathValuesBinder(c *Context) *ValueBinder\necho.QueryParamsBinder(c *Context) *ValueBinder\necho.FormFieldBinder(c *Context) *ValueBinder\n```\n\n## Group methods\n\n```go\ng.Use(m ...MiddlewareFunc)\ng.Group(prefix string, m ...MiddlewareFunc) *Group\ng.GET(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.POST(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.PUT(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.ANY(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) Routes\ng.Static(pathPrefix, fsRoot string, m ...MiddlewareFunc) RouteInfo\ng.StaticFS(pathPrefix string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\ng.File(path, file string, m ...MiddlewareFunc) RouteInfo\ng.FileFS(file string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\n```\n\n## Built-in middleware\n\n| Middleware | Description |\n|---|---|\n| BasicAuth | HTTP basic authentication |\n| BodyDump | Dumps request and response bodies |\n| BodyLimit | Limits request body size |\n| Compress | Gzip response compression |\n| ContextTimeout | Request context timeout |\n| CORS | Cross-origin resource sharing |\n| CSRF | Cross-site request forgery protection |\n| Decompress | Gzip request decompression |\n| KeyAuth | Key-based authentication |\n| MethodOverride | HTTP method override |\n| Proxy | Reverse proxy |\n| RateLimiter | Rate limiting |\n| Recover | Panic recovery |\n| Redirect | HTTP redirect |\n| RequestID | Request ID header |\n| RequestLogger | Structured request logging |\n| Rewrite | URL rewriting |\n| Secure | Security headers |\n| Slash | Trailing slash handling |\n| Static | Static file serving |\n\n## Wrapping stdlib handlers\n\n```go\necho.WrapHandler(h http.Handler) HandlerFunc\necho.WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc\n```\n\n## Virtual host\n\n```go\necho.NewVirtualHostHandler(vhosts map[string]*Echo) *Echo\n```\n\n## echotest package\n\n```go\nimport \"github.com/labstack/echo/v5/echotest\"\n\nechotest.LoadBytes(t *testing.T, name string, opts ...loadBytesOpts) []byte\nechotest.TrimNewlineEnd(bytes []byte) []byte\nechotest.ContextConfig{...}\nechotest.MultipartForm{...}\nechotest.MultipartFormFile{...}\n```\n\n## Complete server example\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"log/slog\"\n    \"net/http\"\n    \"os\"\n    \"os/signal\"\n    \"syscall\"\n    \"time\"\n\n    \"github.com/labstack/echo/v5\"\n    \"github.com/labstack/echo/v5/middleware\"\n)\n\nfunc main() {\n    e := echo.New()\n    e.Use(middleware.RequestLogger())\n    e.Use(middleware.Recover())\n\n    e.GET(\"/\", func(c *echo.Context) error {\n        return c.String(http.StatusOK, \"Hello, World!\")\n    })\n\n    e.GET(\"/users/:id\", func(c *echo.Context) error {\n        id, err := echo.PathParam[int](c, \"id\")\n        if err != nil {\n            return echo.NewHTTPError(http.StatusBadRequest, \"invalid user id\")\n        }\n        return c.JSON(http.StatusOK, map[string]int{\"id\": id})\n    })\n\n    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)\n    defer cancel()\n\n    sc := echo.StartConfig{\n        Address:        \":8080\",\n        GracefulTimeout: 10 * time.Second,\n    }\n    if err := sc.Start(ctx, e); err != nil {\n        slog.Error(\"server error\", \"error\", err)\n    }\n}\n```\n","isInternal":false,"tokens":3003,"sizeBytes":11599},{"name":"llms.txt","path":"llms.txt","rawUrl":"https://raw.githubusercontent.com/labstack/echo/HEAD/llms.txt","title":"LLM Index & Context Digest","category":"root-instruction","format":"text","content":"# Echo\n\n> 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.\n\nKey facts for LLMs:\n- Import path is `github.com/labstack/echo/v5`\n- Handlers use `*echo.Context` (pointer to struct)\n- `Context` is a concrete struct, not an interface\n- Logging uses `log/slog`\n- Go 1.25+ required\n\n## Quick Reference\n\n- [Echo v5 API Changes](https://github.com/labstack/echo/blob/master/API_CHANGES_V5.md): Breaking changes reference\n- [Echo Official Docs](https://echo.labstack.com): Human-readable documentation site\n- [pkg.go.dev](https://pkg.go.dev/github.com/labstack/echo/v5): API reference on Go package discovery\n\n## Core Patterns\n\n### Server setup\n\n```go\npackage main\n\nimport (\n    \"log/slog\"\n    \"net/http\"\n\n    \"github.com/labstack/echo/v5\"\n    \"github.com/labstack/echo/v5/middleware\"\n)\n\nfunc hello(c *echo.Context) error {\n    return c.String(http.StatusOK, \"Hello, World!\")\n}\n\nfunc main() {\n    e := echo.New()\n    e.Use(middleware.RequestLogger())\n    e.Use(middleware.Recover())\n    e.GET(\"/\", hello)\n    if err := e.Start(\":8080\"); err != nil {\n        slog.Error(\"failed to start server\", \"error\", err)\n    }\n}\n```\n\n### Handler signature\n\n```go\nfunc handler(c *echo.Context) error {\n    return c.JSON(http.StatusOK, map[string]string{\"hello\": \"world\"})\n}\n```\n\n### Route registration\n\n```go\ne := echo.New()\ne.GET(\"/users/:id\", getUser)\ne.POST(\"/users\", createUser)\ne.PUT(\"/users/:id\", updateUser)\ne.DELETE(\"/users/:id\", deleteUser)\n\n// Group routes\ng := e.Group(\"/api/v1\")\ng.GET(\"/posts\", listPosts)\ng.POST(\"/posts\", createPost)\n\n// Group with middleware\nadmin := e.Group(\"/admin\", middleware.BasicAuth(basicAuthValidator))\nadmin.GET(\"/stats\", getStats)\n```\n\n### Generic parameter extraction\n\n```go\n// Type-safe path params\nid, err := echo.PathParam[int](c, \"id\")\nname, err := echo.PathParam[string](c, \"name\")\n\n// Query params with defaults\npage, err := echo.QueryParamOr[int](c, \"page\", 1)\ntags, err := echo.QueryParams[string](c, \"tags\")\n\n// Form values\nval, err := echo.FormValue[string](c, \"field\")\nval2, err := echo.FormValueOr[string](c, \"field\", \"default\")\n\n// Supported types: bool, string, int/int8/.../int64, uint/.../uint64,\n// float32/float64, time.Time, time.Duration\n```\n\n### Request binding\n\n```go\ntype User struct {\n    Name  string `json:\"name\"`\n    Email string `json:\"email\"`\n}\n\nfunc createUser(c *echo.Context) error {\n    u := new(User)\n    if err := c.Bind(u); err != nil {\n        return echo.NewHTTPError(http.StatusBadRequest, \"invalid request body\")\n    }\n    if err := c.Validate(u); err != nil {\n        return err\n    }\n    return c.JSON(http.StatusCreated, u)\n}\n```\n\n### Error handling\n\n```go\n// NewHTTPError takes a string message\nerr := echo.NewHTTPError(http.StatusBadRequest, \"invalid input\")\n\n// Custom error handler\ne.HTTPErrorHandler = func(c *echo.Context, err error) {\n    // custom error handling\n}\n\n// DefaultHTTPErrorHandler is a factory (exposeError bool)\ne.HTTPErrorHandler = echo.DefaultHTTPErrorHandler(true)\n```\n\n### Logging\n\n```go\n// Echo uses the standard library slog\ne.Logger = slog.Default()\n\n// In handlers\nc.Logger().Info(\"request\", \"path\", c.Path(), \"method\", c.Request().Method)\n\n// Custom logger\nlogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))\ne.Logger = logger\n```\n\n### Middleware\n\n```go\n// Built-in middleware\ne.Use(middleware.RequestLogger())\ne.Use(middleware.Recover())\ne.Use(middleware.CORS())\ne.Use(middleware.Gzip())\ne.Use(middleware.BasicAuth(func(c *echo.Context, username, password string) (bool, error) {\n    return username == \"admin\" && password == \"secret\", nil\n}))\ne.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(20)))\n\n// Route-level middleware\ne.GET(\"/sensitive\", handler, middleware.RequestID(), middleware.Logger())\n```\n\n### Response helpers\n\n```go\nc.String(http.StatusOK, \"text\")\nc.JSON(http.StatusOK, data)\nc.JSONPretty(http.StatusOK, data, \"  \")\nc.XML(http.StatusOK, data)\nc.Blob(http.StatusOK, \"image/png\", bytes)\nc.Stream(http.StatusOK, \"text/event-stream\", reader)\nc.File(\"path/to/file\")\nc.FileFS(\"file.txt\", filesystem)\nc.Attachment(\"path/to/file\", \"download.txt\")\nc.Redirect(http.StatusFound, \"/new-path\")\nc.NoContent(http.StatusOK)\nc.HTML(http.StatusOK, \"<h1>Hello</h1>\")\n```\n\n### Static files\n\n```go\ne.Static(\"/assets\", \"public\")\ne.StaticFS(\"/static\", filesystem)\ne.File(\"/robots.txt\", \"robots.txt\")\ne.FileFS(\"file.txt\", filesystem)\n```\n\n### Context store (type-safe)\n\n```go\n// Set and get values on context\nc.Set(\"user\", user)\nval, err := echo.ContextGet[*User](c, \"user\")\ncount, err := echo.ContextGetOr[int](c, \"count\", 0)\n```\n\n### Graceful shutdown with StartConfig\n\n```go\nctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)\ndefer cancel()\n\nsc := echo.StartConfig{\n    Address:        \":8080\",\n    GracefulTimeout: 10 * time.Second,\n}\nif err := sc.Start(ctx, e); err != nil {\n    log.Fatal(err)\n}\n```\n\n## Optional\n\n- [Echo GitHub Discussions](https://github.com/labstack/echo/discussions): Community Q&A and proposals\n- [Echo Roadmap](https://github.com/labstack/echo/blob/master/ROADMAP.md): Future plans\n- [Echo Changelog](https://github.com/labstack/echo/blob/master/CHANGELOG.md): Release history\n- [API_CHANGES_V5.md](https://github.com/labstack/echo/blob/master/API_CHANGES_V5.md): Full v5 breaking changes reference\n","isInternal":false,"tokens":1424,"sizeBytes":5523},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/labstack/echo/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## About This Project\n\nEcho is a high performance, minimalist Go web framework. This is the main repository for Echo v5, which is available as a Go module at `github.com/labstack/echo/v5`.\n\n## Development Commands\n\nThe project uses a Makefile for common development tasks:\n\n- `make check` - Run linting, vetting, and race condition tests (default target)\n- `make init` - Install required linting tools (golint, staticcheck)\n- `make lint` - Run staticcheck and golint\n- `make vet` - Run go vet\n- `make test` - Run short tests\n- `make race` - Run tests with race detector\n- `make benchmark` - Run benchmarks\n\nExample commands for development:\n```bash\n# Setup development environment\nmake init\n\n# Run all checks (lint, vet, race)\nmake check\n\n# Run specific tests\ngo test ./middleware/...\ngo test -race ./...\n\n# Run benchmarks\nmake benchmark\n```\n\n## Code Architecture\n\n### Core Components\n\n**Echo Instance (`echo.go`)**\n- The `Echo` struct is the top-level framework instance\n- Contains router, middleware stacks, and server configuration\n- Not goroutine-safe for mutations after server start\n\n**Context (`context.go`)**\n- The `Context` struct represents HTTP request/response context\n- Provides methods for request/response handling, path parameters, data binding\n- Core abstraction for request processing\n\n**Router (`router.go`)**\n- Radix tree-based HTTP router with smart route prioritization\n- Supports static routes, parameterized routes (`/users/:id`), and wildcard routes (`/static/*`)\n- Each HTTP method has its own routing tree\n\n**Middleware (`middleware/`)**\n- Extensive middleware system with 50+ built-in middlewares\n- Middleware can be applied at Echo, Group, or individual route level\n- Common middleware: Logger, Recover, CORS, JWT, Rate Limiting, etc.\n\n### Key Patterns\n\n**Middleware Chain**\n- Pre-middleware runs before routing\n- Regular middleware runs after routing but before handlers\n- Middleware functions have signature `func(next echo.HandlerFunc) echo.HandlerFunc`\n\n**Route Groups**\n- Routes can be grouped with common prefixes and middleware\n- Groups support nested sub-groups\n- Defined in `group.go`\n\n**Data Binding**\n- Automatic binding of request data (JSON, XML, form) to Go structs\n- Implemented in `binder.go` with support for custom binders\n\n**Error Handling**\n- Centralized error handling via `HTTPErrorHandler`\n- Automatic panic recovery with stack traces\n\n## File Organization\n\n- Root directory: Core Echo functionality (echo.go, context.go, router.go, etc.)\n- `middleware/`: All built-in middleware implementations\n- `_test/`: Test fixtures and utilities\n- `_fixture/`: Test data files\n\n## Code Style\n\n- Go code uses tabs for indentation (per .editorconfig)\n- Follows standard Go conventions and formatting\n- Uses gofmt, golint, and staticcheck for code quality\n\n## Testing\n\n- Standard Go testing with `testing` package\n- Tests include unit tests, integration tests, and benchmarks\n- Race condition testing is required (`make race`)\n- Test files follow `*_test.go` naming convention","isInternal":false,"tokens":701,"sizeBytes":3132}],"systemPromptSnippet":"<yakaai_skills repo=\"labstack/echo\">\n<!-- File: llms-full.txt (Tokens: ~3003 | Category: root-instruction) -->\n# Echo\n\n> 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.\n\nImport path: `github.com/labstack/echo/v5`\nGo version: 1.25+\nHandler signature: `func(c *echo.Context) error`\n\n## Context\n\n`Context` is a concrete struct passed by pointer to all handlers.\n\n```go\ntype Context struct { ... }\n\n// Handlers receive *echo.Context\nfunc handler(c *echo.Context) error { ... }\n```\n\n### Context methods\n\n```go\nc.Request() *http.Request\nc.SetRequest(r *http.Request)\nc.Response() http.ResponseWriter\nc.SetResponse(rw http.ResponseWriter)\nc.IsTLS() bool\nc.IsWebSocket() bool\nc.Scheme() string\nc.RealIP() string\nc.Path() string\nc.SetPath(path string)\nc.RouteInfo() RouteInfo\nc.Param(name string) string\nc.ParamOr(name, defaultValue string) string\nc.PathValues() PathValues\nc.SetPathValues(pathValues PathValues)\nc.QueryParam(name string) string\nc.QueryParamOr(name, defaultValue string) string\nc.QueryParams() []string\nc.QueryString() string\nc.FormValue(name string) string\nc.FormValueOr(name, defaultValue string) string\nc.FormValues() []string\nc.FormFile(name string) (*multipart.FileHeader, error)\nc.MultipartForm() (*multipart.Form, error)\nc.Cookie(name string) (*http.Cookie, error)\nc.SetCookie(cookie *http.Cookie)\nc.Cookies() []*http.Cookie\nc.Get(key string) any\nc.Set(key string, val any)\nc.Bind(i any) error\nc.Validate(i any) error\nc.Render(code int, name string, data any) error\nc.HTML(code int, html string) error\nc.HTMLBlob(code int, b []byte) error\nc.String(code int, s string) error\nc.JSON(code int, i any) error\nc.JSONPretty(code int, i any, indent string) error\nc.JSONBlob(code int, b []byte) error\nc.JSONP(code int, callback string, i any) error\nc.JSONPBlob(code int, callback string, i any) error\nc.XML(code int, i any) error\nc.XMLPretty(code int, i any, indent string) error\nc.XMLBlob(code int, b []byte) error\nc.Blob(code int, contentType string, b []byte) error\nc.Stream(code int, contentType string, r io.Reader) error\nc.File(file string) error\nc.FileFS(file string, filesystem fs.FS) error\nc.Attachment(file, name string) error\nc.Inline(file, name string) error\nc.NoContent(code int) error\nc.Redirect(code int, url string) error\nc.Logger() *slog.Logger\nc.SetLogger(logger *slog.Logger)\nc.Echo() *Echo\n```\n\n### Context store (type-safe)\n\n```go\nc.Set(\"user\", user)\nval, err := echo.ContextGet[*User](c, \"user\")\ncount, err := echo.ContextGetOr[int](c, \"count\", 0)\n```\n\n## Echo struct\n\n```go\ntype Echo struct {\n    Binder           Binder\n    Filesystem       fs.FS\n    Renderer         Renderer\n    Validator        Validator\n    JSONSerializer   JSONSerializer\n    IPExtractor      IPExtractor\n    OnAddRoute       func(route Route) error\n    HTTPErrorHandler HTTPErrorHandler\n    Logger           *slog.Logger\n}\n```\n\nConstructor: `echo.New() *Echo` or `echo.NewWithConfig(config Config) *Echo`\n\n## Echo methods\n\n### Routing\n\n```go\ne.GET(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.POST(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.PUT(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.ANY(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) Routes\ne.RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ne.AddRoute(route Route) (RouteInfo, error)\n```\n\n### Middleware\n\n```go\ne.Use(middleware ...MiddlewareFunc)\ne.Pre(middleware ...MiddlewareFunc)\ne.Middlewares() []MiddlewareFunc\ne.PreMiddlewares() []MiddlewareFunc\n```\n\n### Groups\n\n```go\ne.Group(prefix string, m ...MiddlewareFunc) *Group\n```\n\n### Static files\n\n```go\ne.Static(pathPrefix, fsRoot string, m ...MiddlewareFunc) RouteInfo\ne.StaticFS(pathPrefix string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\ne.File(path, file string, m ...MiddlewareFunc) RouteInfo\ne.FileFS(file string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\n```\n\n### Server\n\n```go\ne.Start(address string) error\ne.StartTLS(address string, certFile, keyFile any) error\n```\n\n```go\ntype StartConfig struct {\n    Address          string\n    HideBanner       bool\n    HidePort         bool\n    CertFilesystem   fs.FS\n    TLSConfig        *tls.Config\n    ListenerNetwork  string\n    ListenerAddrFunc func(addr net.Addr)\n    GracefulTimeout  time.Duration\n    OnShutdownError  func(err error)\n    BeforeServeFunc  func(s *http.Server) error\n}\n\n// StartConfig.Start and StartConfig.StartTLS for graceful shutdown\nsc := echo.StartConfig{Address: \":8080\", GracefulTimeout: 10 * time.Second}\nsc.Start(ctx, e)\n```\n\n## Error handling\n\n```go\n// HTTPError with string message\nerr := echo.NewHTTPError(http.StatusBadRequest, \"invalid input\")\n\n// Custom error handler (params: context pointer, then error)\ne.HTTPErrorHandler = func(c *echo.Context, err error) {\n    // custom error handling\n}\n\n// Default error handler factory (exposeError bool)\ne.HTTPErrorHandler = echo.DefaultHTTPErrorHandler(true)\n```\n\n```go\necho.ErrBadRequest\necho.ErrValidatorNotRegistered\necho.ErrInvalidKeyType\necho.ErrNonExistentKey\n```\n\n## Logging\n\nEcho uses the standard library `log/slog` package.\n\n```go\ne.Logger = slog.Default()\nc.Logger().Info(\"message\", \"key\", \"value\")\n\n// Custom logger\nlogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))\ne.Logger = logger\n```\n\n## Router\n\n```go\n// Router interface with DefaultRouter implementation\nfunc NewRouter(config RouterConfig) *DefaultRouter\nfunc (e *Echo) Router() Router\n\n// Thread-safe routing\nfunc NewConcurrentRouter(r Router) Router\n```\n\n## RouteInfo\n\n```go\ntype RouteInfo struct {\n    Name       string\n    Method     string\n    Path       string\n    Parameters []string\n}\n\n// Routes type with filtering methods\ntype Routes []RouteInfo\n// Methods: FilterByMethod, FilterByName, FilterByPath, FindByMethodPath, Reverse\n```\n\n## PathValues\n\n```go\ntype PathValue struct {\n    Name  string\n    Value string\n}\ntype PathValues []PathValue\n\nfunc (p PathValues) Get(name string) (string, bool)\nfunc (p PathValues) GetOr(name string, defaultValue string) string\n```\n\n## Generic helper functions\n\n```go\necho.PathParam[T](c *Context, name string, opts ...any) (T, error)\necho.PathParamOr[T](c *Context, name string, defaultValue T, opts ...any) (T, error)\necho.QueryParam[T](c *Context, key string, opts ...any) (T, error)\necho.QueryParamOr[T](c *Context, key string, defaultValue T, opts ...any) (T, error)\necho.QueryParams[T](c *Context, key string, opts ...any) ([]T, error)\necho.QueryParamsOr[T](c *Context, key string, defaultValue []T, opts ...any) ([]T, error)\necho.FormValue[T](c *Context, key string, opts ...any) (T, error)\necho.FormValueOr[T](c *Context, key string, defaultValue T, opts ...any) (T, error)\necho.FormValues[T](c *Context, key string, opts ...any) ([]T, error)\necho.FormValuesOr[T](c *Context, key string, defaultValue []T, opts ...any) ([]T, error)\necho.ParseValue[T](value string, opts ...any) (T, error)\necho.ParseValueOr[T](value string, defaultValue T, opts ...any) (T, error)\necho.ParseValues[T](values []string, opts ...any) ([]T, error)\necho.ParseValuesOr[T](values []string, defaultValue []T, opts ...any) ([]T, error)\necho.ContextGet[T](c *Context, key string) (T, error)\necho.ContextGetOr[T](c *Context, key string, defaultValue T) (T, error)\n```\n\nSupported 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\n\n## Binding functions\n\n```go\necho.BindBody(c *Context, target any) error\necho.BindHeaders(c *Context, target any) error\necho.BindPathValues(c *Context, target any) error\necho.BindQueryParams(c *Context, target any) error\n```\n\n### Binder helpers\n\n```go\necho.PathValuesBinder(c *Context) *ValueBinder\necho.QueryParamsBinder(c *Context) *ValueBinder\necho.FormFieldBinder(c *Context) *ValueBinder\n```\n\n## Group methods\n\n```go\ng.Use(m ...MiddlewareFunc)\ng.Group(prefix string, m ...MiddlewareFunc) *Group\ng.GET(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.POST(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.PUT(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.ANY(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo\ng.Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) Routes\ng.Static(pathPrefix, fsRoot string, m ...MiddlewareFunc) RouteInfo\ng.StaticFS(pathPrefix string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\ng.File(path, file string, m ...MiddlewareFunc) RouteInfo\ng.FileFS(file string, filesystem fs.FS, m ...MiddlewareFunc) RouteInfo\n```\n\n## Built-in middleware\n\n| Middleware | Description |\n|---|---|\n| BasicAuth | HTTP basic authentication |\n| BodyDump | Dumps request and response bodies |\n| BodyLimit | Limits request body size |\n| Compress | Gzip response compression |\n| ContextTimeout | Request context timeout |\n| CORS | Cross-origin resource sharing |\n| CSRF | Cross-site request forgery protection |\n| Decompress | Gzip request decompression |\n| KeyAuth | Key-based authentication |\n| MethodOverride | HTTP method override |\n| Proxy | Reverse proxy |\n| RateLimiter | Rate limiting |\n| Recover | Panic recovery |\n| Redirect | HTTP redirect |\n| RequestID | Request ID header |\n| RequestLogger | Structured request logging |\n| Rewrite | URL rewriting |\n| Secure | Security headers |\n| Slash | Trailing slash handling |\n| Static | Static file serving |\n\n## Wrapping stdlib handlers\n\n```go\necho.WrapHandler(h http.Handler) HandlerFunc\necho.WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc\n```\n\n## Virtual host\n\n```go\necho.NewVirtualHostHandler(vhosts map[string]*Echo) *Echo\n```\n\n## echotest package\n\n```go\nimport \"github.com/labstack/echo/v5/echotest\"\n\nechotest.LoadBytes(t *testing.T, name string, opts ...loadBytesOpts) []byte\nechotest.TrimNewlineEnd(bytes []byte) []byte\nechotest.ContextConfig{...}\nechotest.MultipartForm{...}\nechotest.MultipartFormFile{...}\n```\n\n## Complete server example\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"log/slog\"\n    \"net/http\"\n    \"os\"\n    \"os/signal\"\n    \"syscall\"\n    \"time\"\n\n    \"github.com/labstack/echo/v5\"\n    \"github.com/labstack/echo/v5/middleware\"\n)\n\nfunc main() {\n    e := echo.New()\n    e.Use(middleware.RequestLogger())\n    e.Use(middleware.Recover())\n\n    e.GET(\"/\", func(c *echo.Context) error {\n        return c.String(http.StatusOK, \"Hello, World!\")\n    })\n\n    e.GET(\"/users/:id\", func(c *echo.Context) error {\n        id, err := echo.PathParam[int](c, \"id\")\n        if err != nil {\n            return echo.NewHTTPError(http.StatusBadRequest, \"invalid user id\")\n        }\n        return c.JSON(http.StatusOK, map[string]int{\"id\": id})\n    })\n\n    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)\n    defer cancel()\n\n    sc := echo.StartConfig{\n        Address:        \":8080\",\n        GracefulTimeout: 10 * time.Second,\n    }\n    if err := sc.Start(ctx, e); err != nil {\n        slog.Error(\"server error\", \"error\", err)\n    }\n}\n```\n\n<!-- File: llms.txt (Tokens: ~1424 | Category: root-instruction) -->\n# Echo\n\n> 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.\n\nKey facts for LLMs:\n- Import path is `github.com/labstack/echo/v5`\n- Handlers use `*echo.Context` (pointer to struct)\n- `Context` is a concrete struct, not an interface\n- Logging uses `log/slog`\n- Go 1.25+ required\n\n## Quick Reference\n\n- [Echo v5 API Changes](https://github.com/labstack/echo/blob/master/API_CHANGES_V5.md): Breaking changes reference\n- [Echo Official Docs](https://echo.labstack.com): Human-readable documentation site\n- [pkg.go.dev](https://pkg.go.dev/github.com/labstack/echo/v5): API reference on Go package discovery\n\n## Core Patterns\n\n### Server setup\n\n```go\npackage main\n\nimport (\n    \"log/slog\"\n    \"net/http\"\n\n    \"github.com/labstack/echo/v5\"\n    \"github.com/labstack/echo/v5/middleware\"\n)\n\nfunc hello(c *echo.Context) error {\n    return c.String(http.StatusOK, \"Hello, World!\")\n}\n\nfunc main() {\n    e := echo.New()\n    e.Use(middleware.RequestLogger())\n    e.Use(middleware.Recover())\n    e.GET(\"/\", hello)\n    if err := e.Start(\":8080\"); err != nil {\n        slog.Error(\"failed to start server\", \"error\", err)\n    }\n}\n```\n\n### Handler signature\n\n```go\nfunc handler(c *echo.Context) error {\n    return c.JSON(http.StatusOK, map[string]string{\"hello\": \"world\"})\n}\n```\n\n### Route registration\n\n```go\ne := echo.New()\ne.GET(\"/users/:id\", getUser)\ne.POST(\"/users\", createUser)\ne.PUT(\"/users/:id\", updateUser)\ne.DELETE(\"/users/:id\", deleteUser)\n\n// Group routes\ng := e.Group(\"/api/v1\")\ng.GET(\"/posts\", listPosts)\ng.POST(\"/posts\", createPost)\n\n// Group with middleware\nadmin := e.Group(\"/admin\", middleware.BasicAuth(basicAuthValidator))\nadmin.GET(\"/stats\", getStats)\n```\n\n### Generic parameter extraction\n\n```go\n// Type-safe path params\nid, err := echo.PathParam[int](c, \"id\")\nname, err := echo.PathParam[string](c, \"name\")\n\n// Query params with defaults\npage, err := echo.QueryParamOr[int](c, \"page\", 1)\ntags, err := echo.QueryParams[string](c, \"tags\")\n\n// Form values\nval, err := echo.FormValue[string](c, \"field\")\nval2, err := echo.FormValueOr[string](c, \"field\", \"default\")\n\n// Supported types: bool, string, int/int8/.../int64, uint/.../uint64,\n// float32/float64, time.Time, time.Duration\n```\n\n### Request binding\n\n```go\ntype User struct {\n    Name  string `json:\"name\"`\n    Email string `json:\"email\"`\n}\n\nfunc createUser(c *echo.Context) error {\n    u := new(User)\n    if err := c.Bind(u); err != nil {\n        return echo.NewHTTPError(http.StatusBadRequest, \"invalid request body\")\n    }\n    if err := c.Validate(u); err != nil {\n        return err\n    }\n    return c.JSON(http.StatusCreated, u)\n}\n```\n\n### Error handling\n\n```go\n// NewHTTPError takes a string message\nerr := echo.NewHTTPError(http.StatusBadRequest, \"invalid input\")\n\n// Custom error handler\ne.HTTPErrorHandler = func(c *echo.Context, err error) {\n    // custom error handling\n}\n\n// DefaultHTTPErrorHandler is a factory (exposeError bool)\ne.HTTPErrorHandler = echo.DefaultHTTPErrorHandler(true)\n```\n\n### Logging\n\n```go\n// Echo uses the standard library slog\ne.Logger = slog.Default()\n\n// In handlers\nc.Logger().Info(\"request\", \"path\", c.Path(), \"method\", c.Request().Method)\n\n// Custom logger\nlogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))\ne.Logger = logger\n```\n\n### Middleware\n\n```go\n// Built-in middleware\ne.Use(middleware.RequestLogger())\ne.Use(middleware.Recover())\ne.Use(middleware.CORS())\ne.Use(middleware.Gzip())\ne.Use(middleware.BasicAuth(func(c *echo.Context, username, password string) (bool, error) {\n    return username == \"admin\" && password == \"secret\", nil\n}))\ne.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(20)))\n\n// Route-level middleware\ne.GET(\"/sensitive\", handler, middleware.RequestID(), middleware.Logger())\n```\n\n### Response helpers\n\n```go\nc.String(http.StatusOK, \"text\")\nc.JSON(http.StatusOK, data)\nc.JSONPretty(http.StatusOK, data, \"  \")\nc.XML(http.StatusOK, data)\nc.Blob(http.StatusOK, \"image/png\", bytes)\nc.Stream(http.StatusOK, \"text/event-stream\", reader)\nc.File(\"path/to/file\")\nc.FileFS(\"file.txt\", filesystem)\nc.Attachment(\"path/to/file\", \"download.txt\")\nc.Redirect(http.StatusFound, \"/new-path\")\nc.NoContent(http.StatusOK)\nc.HTML(http.StatusOK, \"<h1>Hello</h1>\")\n```\n\n### Static files\n\n```go\ne.Static(\"/assets\", \"public\")\ne.StaticFS(\"/static\", filesystem)\ne.File(\"/robots.txt\", \"robots.txt\")\ne.FileFS(\"file.txt\", filesystem)\n```\n\n### Context store (type-safe)\n\n```go\n// Set and get values on context\nc.Set(\"user\", user)\nval, err := echo.ContextGet[*User](c, \"user\")\ncount, err := echo.ContextGetOr[int](c, \"count\", 0)\n```\n\n### Graceful shutdown with StartConfig\n\n```go\nctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)\ndefer cancel()\n\nsc := echo.StartConfig{\n    Address:        \":8080\",\n    GracefulTimeout: 10 * time.Second,\n}\nif err := sc.Start(ctx, e); err != nil {\n    log.Fatal(err)\n}\n```\n\n## Optional\n\n- [Echo GitHub Discussions](https://github.com/labstack/echo/discussions): Community Q&A and proposals\n- [Echo Roadmap](https://github.com/labstack/echo/blob/master/ROADMAP.md): Future plans\n- [Echo Changelog](https://github.com/labstack/echo/blob/master/CHANGELOG.md): Release history\n- [API_CHANGES_V5.md](https://github.com/labstack/echo/blob/master/API_CHANGES_V5.md): Full v5 breaking changes reference\n\n<!-- File: CLAUDE.md (Tokens: ~701 | Category: claude-rule) -->\n# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## About This Project\n\nEcho is a high performance, minimalist Go web framework. This is the main repository for Echo v5, which is available as a Go module at `github.com/labstack/echo/v5`.\n\n## Development Commands\n\nThe project uses a Makefile for common development tasks:\n\n- `make check` - Run linting, vetting, and race condition tests (default target)\n- `make init` - Install required linting tools (golint, staticcheck)\n- `make lint` - Run staticcheck and golint\n- `make vet` - Run go vet\n- `make test` - Run short tests\n- `make race` - Run tests with race detector\n- `make benchmark` - Run benchmarks\n\nExample commands for development:\n```bash\n# Setup development environment\nmake init\n\n# Run all checks (lint, vet, race)\nmake check\n\n# Run specific tests\ngo test ./middleware/...\ngo test -race ./...\n\n# Run benchmarks\nmake benchmark\n```\n\n## Code Architecture\n\n### Core Components\n\n**Echo Instance (`echo.go`)**\n- The `Echo` struct is the top-level framework instance\n- Contains router, middleware stacks, and server configuration\n- Not goroutine-safe for mutations after server start\n\n**Context (`context.go`)**\n- The `Context` struct represents HTTP request/response context\n- Provides methods for request/response handling, path parameters, data binding\n- Core abstraction for request processing\n\n**Router (`router.go`)**\n- Radix tree-based HTTP router with smart route prioritization\n- Supports static routes, parameterized routes (`/users/:id`), and wildcard routes (`/static/*`)\n- Each HTTP method has its own routing tree\n\n**Middleware (`middleware/`)**\n- Extensive middleware system with 50+ built-in middlewares\n- Middleware can be applied at Echo, Group, or individual route level\n- Common middleware: Logger, Recover, CORS, JWT, Rate Limiting, etc.\n\n### Key Patterns\n\n**Middleware Chain**\n- Pre-middleware runs before routing\n- Regular middleware runs after routing but before handlers\n- Middleware functions have signature `func(next echo.HandlerFunc) echo.HandlerFunc`\n\n**Route Groups**\n- Routes can be grouped with common prefixes and middleware\n- Groups support nested sub-groups\n- Defined in `group.go`\n\n**Data Binding**\n- Automatic binding of request data (JSON, XML, form) to Go structs\n- Implemented in `binder.go` with support for custom binders\n\n**Error Handling**\n- Centralized error handling via `HTTPErrorHandler`\n- Automatic panic recovery with stack traces\n\n## File Organization\n\n- Root directory: Core Echo functionality (echo.go, context.go, router.go, etc.)\n- `middleware/`: All built-in middleware implementations\n- `_test/`: Test fixtures and utilities\n- `_fixture/`: Test data files\n\n## Code Style\n\n- Go code uses tabs for indentation (per .editorconfig)\n- Follows standard Go conventions and formatting\n- Uses gofmt, golint, and staticcheck for code quality\n\n## Testing\n\n- Standard Go testing with `testing` package\n- Tests include unit tests, integration tests, and benchmarks\n- Race condition testing is required (`make race`)\n- Test files follow `*_test.go` naming convention\n</yakaai_skills>"}