echo adapter
The echo adapter wires jetwarp’s portable adapter.Adapter API to the
labstack/echo router (Echo v5).
It’s a solid choice if your team already uses Echo, but you still want your route registration and middleware composition to stay portable across other adapters (stdlib, chi, gin, fiber, …).
Install
Add the adapter module to your project:
go get codeberg.org/iaconlabs/jetwarp/adapter/echo@latest
This pulls in:
codeberg.org/iaconlabs/jetwarp(core adapter + registry)codeberg.org/iaconlabs/jetwarp/drivers/v1/echo(the Echo v5 driver)codeberg.org/labstack/echo/v5(Echo itself)
Create a router
package main
import (
"log"
"net/http"
twecho "codeberg.org/iaconlabs/jetwarp/adapter/echo"
)
func main() {
r := twecho.New()
r.HandleFunc(http.MethodGet, "/healthz", func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("ok"))
})
// Always check accumulated registration errors before serving traffic.
if err := r.Err(); err != nil {
log.Fatal(err)
}
log.Fatal(http.ListenAndServe(":8080", r))
}
Route patterns and parameters
jetwarp’s canonical pattern syntax uses ServeMux-style {name} parameters.
Echo uses :name internally, but you keep writing the canonical form:
r.HandleFunc(http.MethodGet, "/users/{id}", func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
_, _ = w.Write([]byte("id=" + id))
})
Why PathValue works with Echo
Echo exposes params through its own context API (c.Param(...)), not via *http.Request.
The jetwarp Echo driver bridges captured parameters into the request before calling your handler, so you can read them portably via:
id := r.PathValue("id")
This is the recommended application-level approach when you want to keep handlers adapter-agnostic.
MethodAny ("*") support
jetwarp supports method-agnostic routes via the canonical method token "*":
r.HandleFunc("*", "/any", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("ANY:" + r.Method))
})
Echo has a dedicated ANY method route type, and it is intentionally lower priority than method-specific routes. The driver relies on that behavior so the portability rule holds:
- explicit methods (e.g.
GET /any) must win over* /any, regardless of registration order
Middleware with Echo
Prefer portable net/http middleware
Echo middleware is not the same shape as portable net/http middleware. For maximum portability, prefer standard
net/http middleware and wrap it with adapter.HTTP(...) / adapter.HTTPNamed(...):
import (
"net/http"
"codeberg.org/iaconlabs/jetwarp/adapter"
)
r.Use(adapter.HTTPNamed("example", func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// before
next.ServeHTTP(w, r)
// after
})
}))
Ordering is deterministic across adapters:
Use → Group → With → per-route → handler
Gotcha: Echo defaults are not installed
The Echo driver constructs the engine with echo.New() (not a preconfigured “defaults” bundle).
That means you should install any recovery/logging/auth middleware explicitly.
This tends to work well with jetwarp’s philosophy: middleware should be visible in the setup code, and portable by default.
Important gotchas
1) No in-segment suffix/prefix params
Echo-style routers would interpret a route like :id.json as a parameter named id.json.
To avoid silently wrong behavior, the Echo driver rejects in-segment suffix/prefix patterns such as:
/files/{id}.json/prefix-{id}
Use a plain segment param instead and validate in the handler:
- ✅
/files/{id}then parse/validate - ❌
/files/{id}.json(not supported on Echo adapter)
2) Literal segments may not start with : or *
Echo reserves : and * prefixes in path segments. The driver rejects literal segments that start with those prefixes
to avoid registering routes that Echo would interpret as params/wildcards.
If you really need a literal segment that begins with : or *, consider URL-encoding it at the application level,
or choose a different path shape.
Native echo middleware (mw/echo, v1.2+)
The mw/echo module lets you register an echo.MiddlewareFunc through jetwarp’s adapter API.
This keeps the registration inside jetwarp’s setup flow and gives you access to echo.Context
features — response writer, logger, binder — that are not available through standard net/http.
import (
echov5 "github.com/labstack/echo/v5"
mwecho "codeberg.org/iaconlabs/jetwarp/mw/echo"
)
r.Use(mwecho.Middleware(func(next echov5.HandlerFunc) echov5.HandlerFunc {
return func(c echov5.Context) error {
// echo.Context is available here
return next(c)
}
}, "my-echo-mw"))
On any non-echo adapter, Apply() returns ErrNativeMWUnsupported and the error is
recorded in Err(). The route is still registered and served normally.
Scope note: unlike Fiber, Echo does not support path-prefix-scoped
Use(). Native middleware registered on a scoped router (e.g.r.Group("/api")) applies globally, matching the documented v1.2 limitation for gin, echo, and chi.
Accessing the underlying engine (escape hatch)
For debugging or Echo-specific integration, you can access the underlying *echo.Echo engine via an optional escape hatch.
Be careful: registering routes directly on the engine bypasses jetwarp’s registry (and therefore OpenAPI generation and some tooling).
import echov5 "codeberg.org/labstack/echo/v5"
type engineProvider interface {
Engine() any
}
if ep, ok := r.(engineProvider); ok {
if e, ok := ep.Engine().(*echov5.Echo); ok {
_ = e // Echo engine
}
}