Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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
	}
}

See also