diff --git a/llms-full.txt b/llms-full.txt new file mode 100644 index 000000000..b6db08055 --- /dev/null +++ b/llms-full.txt @@ -0,0 +1,401 @@ +# 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 + +```go +c.Request() *http.Request +c.SetRequest(r *http.Request) +c.Response() http.ResponseWriter +c.SetResponse(rw http.ResponseWriter) +c.IsTLS() bool +c.IsWebSocket() bool +c.Scheme() string +c.RealIP() string +c.Path() string +c.SetPath(path string) +c.RouteInfo() RouteInfo +c.Param(name string) string +c.ParamOr(name, defaultValue string) string +c.PathValues() PathValues +c.SetPathValues(pathValues PathValues) +c.QueryParam(name string) string +c.QueryParamOr(name, defaultValue string) string +c.QueryParams() []string +c.QueryString() string +c.FormValue(name string) string +c.FormValueOr(name, defaultValue string) string +c.FormValues() []string +c.FormFile(name string) (*multipart.FileHeader, error) +c.MultipartForm() (*multipart.Form, error) +c.Cookie(name string) (*http.Cookie, error) +c.SetCookie(cookie *http.Cookie) +c.Cookies() []*http.Cookie +c.Get(key string) any +c.Set(key string, val any) +c.Bind(i any) error +c.Validate(i any) error +c.Render(code int, name string, data any) error +c.HTML(code int, html string) error +c.HTMLBlob(code int, b []byte) error +c.String(code int, s string) error +c.JSON(code int, i any) error +c.JSONPretty(code int, i any, indent string) error +c.JSONBlob(code int, b []byte) error +c.JSONP(code int, callback string, i any) error +c.JSONPBlob(code int, callback string, i any) error +c.XML(code int, i any) error +c.XMLPretty(code int, i any, indent string) error +c.XMLBlob(code int, b []byte) error +c.Blob(code int, contentType string, b []byte) error +c.Stream(code int, contentType string, r io.Reader) error +c.File(file string) error +c.FileFS(file string, filesystem fs.FS) error +c.Attachment(file, name string) error +c.Inline(file, name string) error +c.NoContent(code int) error +c.Redirect(code int, url string) error +c.Logger() *slog.Logger +c.SetLogger(logger *slog.Logger) +c.Echo() *Echo +``` + +### 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) + } +} +``` diff --git a/llms.txt b/llms.txt new file mode 100644 index 000000000..7071adcf8 --- /dev/null +++ b/llms.txt @@ -0,0 +1,215 @@ +# 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. + +Key facts for LLMs: +- Import path is `github.com/labstack/echo/v5` +- Handlers use `*echo.Context` (pointer to struct) +- `Context` is a concrete struct, not an interface +- Logging uses `log/slog` +- Go 1.25+ required + +## Quick Reference + +- [Echo v5 API Changes](https://github.com/labstack/echo/blob/master/API_CHANGES_V5.md): Breaking changes reference +- [Echo Official Docs](https://echo.labstack.com): Human-readable documentation site +- [pkg.go.dev](https://pkg.go.dev/github.com/labstack/echo/v5): API reference on Go package discovery + +## Core Patterns + +### Server setup + +```go +package main + +import ( + "log/slog" + "net/http" + + "github.com/labstack/echo/v5" + "github.com/labstack/echo/v5/middleware" +) + +func hello(c *echo.Context) error { + return c.String(http.StatusOK, "Hello, World!") +} + +func main() { + e := echo.New() + e.Use(middleware.RequestLogger()) + e.Use(middleware.Recover()) + e.GET("/", hello) + if err := e.Start(":8080"); err != nil { + slog.Error("failed to start server", "error", err) + } +} +``` + +### Handler signature + +```go +func handler(c *echo.Context) error { + return c.JSON(http.StatusOK, map[string]string{"hello": "world"}) +} +``` + +### Route registration + +```go +e := echo.New() +e.GET("/users/:id", getUser) +e.POST("/users", createUser) +e.PUT("/users/:id", updateUser) +e.DELETE("/users/:id", deleteUser) + +// Group routes +g := e.Group("/api/v1") +g.GET("/posts", listPosts) +g.POST("/posts", createPost) + +// Group with middleware +admin := e.Group("/admin", middleware.BasicAuth(basicAuthValidator)) +admin.GET("/stats", getStats) +``` + +### Generic parameter extraction + +```go +// Type-safe path params +id, err := echo.PathParam[int](c, "id") +name, err := echo.PathParam[string](c, "name") + +// Query params with defaults +page, err := echo.QueryParamOr[int](c, "page", 1) +tags, err := echo.QueryParams[string](c, "tags") + +// Form values +val, err := echo.FormValue[string](c, "field") +val2, err := echo.FormValueOr[string](c, "field", "default") + +// Supported types: bool, string, int/int8/.../int64, uint/.../uint64, +// float32/float64, time.Time, time.Duration +``` + +### Request binding + +```go +type User struct { + Name string `json:"name"` + Email string `json:"email"` +} + +func createUser(c *echo.Context) error { + u := new(User) + if err := c.Bind(u); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "invalid request body") + } + if err := c.Validate(u); err != nil { + return err + } + return c.JSON(http.StatusCreated, u) +} +``` + +### Error handling + +```go +// NewHTTPError takes a string message +err := echo.NewHTTPError(http.StatusBadRequest, "invalid input") + +// Custom error handler +e.HTTPErrorHandler = func(c *echo.Context, err error) { + // custom error handling +} + +// DefaultHTTPErrorHandler is a factory (exposeError bool) +e.HTTPErrorHandler = echo.DefaultHTTPErrorHandler(true) +``` + +### Logging + +```go +// Echo uses the standard library slog +e.Logger = slog.Default() + +// In handlers +c.Logger().Info("request", "path", c.Path(), "method", c.Request().Method) + +// Custom logger +logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) +e.Logger = logger +``` + +### Middleware + +```go +// Built-in middleware +e.Use(middleware.RequestLogger()) +e.Use(middleware.Recover()) +e.Use(middleware.CORS()) +e.Use(middleware.Gzip()) +e.Use(middleware.BasicAuth(func(c *echo.Context, username, password string) (bool, error) { + return username == "admin" && password == "secret", nil +})) +e.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(20))) + +// Route-level middleware +e.GET("/sensitive", handler, middleware.RequestID(), middleware.Logger()) +``` + +### Response helpers + +```go +c.String(http.StatusOK, "text") +c.JSON(http.StatusOK, data) +c.JSONPretty(http.StatusOK, data, " ") +c.XML(http.StatusOK, data) +c.Blob(http.StatusOK, "image/png", bytes) +c.Stream(http.StatusOK, "text/event-stream", reader) +c.File("path/to/file") +c.FileFS("file.txt", filesystem) +c.Attachment("path/to/file", "download.txt") +c.Redirect(http.StatusFound, "/new-path") +c.NoContent(http.StatusOK) +c.HTML(http.StatusOK, "

Hello

") +``` + +### Static files + +```go +e.Static("/assets", "public") +e.StaticFS("/static", filesystem) +e.File("/robots.txt", "robots.txt") +e.FileFS("file.txt", filesystem) +``` + +### Context store (type-safe) + +```go +// Set and get values on context +c.Set("user", user) +val, err := echo.ContextGet[*User](c, "user") +count, err := echo.ContextGetOr[int](c, "count", 0) +``` + +### Graceful shutdown with StartConfig + +```go +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 { + log.Fatal(err) +} +``` + +## Optional + +- [Echo GitHub Discussions](https://github.com/labstack/echo/discussions): Community Q&A and proposals +- [Echo Roadmap](https://github.com/labstack/echo/blob/master/ROADMAP.md): Future plans +- [Echo Changelog](https://github.com/labstack/echo/blob/master/CHANGELOG.md): Release history +- [API_CHANGES_V5.md](https://github.com/labstack/echo/blob/master/API_CHANGES_V5.md): Full v5 breaking changes reference