- Add comprehensive Echo v5 migration guide (ECHO_V5_MIGRATION.md) - Documents all API changes and type signature updates - Provides step-by-step fixes for deprecated middleware - Includes middleware pattern examples for v5 - Documents WebSocket fix for v5 compatibility - Includes verification and rollback plans - Remove outdated infrastructure enhancement plan (3488 lines) - Legacy plan is no longer relevant after Echo v5 migration - Consolidates documentation into single migration guide
7.6 KiB
Echo v5 Migration Plan
Overview
This document outlines the steps required to migrate from Echo v4 to Echo v5, including API changes, type signature updates, and middleware modifications.
Errors Identified
1. Deprecated Middleware
Error: echomiddleware.Logger undefined
Solution: Replace echomiddleware.Logger() with echomiddleware.RequestLogger()
Files affected:
cmd/server/main.go(line 142)internal/router/router.go(line 144)
2. Removed API Methods
Error: a.echo.Close undefined (type *echo.Echo has no field or method Close)
Solution: Remove the echo.Close() call as v5 uses different graceful shutdown mechanism
Files affected:
internal/app/app.go(line 86)
3. Type Signature & Response API Changes
Error: Multiple type mismatches and Response API changes in Echo v5
Root Cause: Echo v5 uses *echo.Context (pointer) for handlers but some code uses echo.Context (value). Response wrapper API also changed.
Solution:
- Update all middleware to use
*echo.Context - Update helper functions to accept
*echo.Context - Fix Response wrapper usage for Echo v5 API
Files affected:
internal/middleware/device_auth.go(lines 38, 170, 212)internal/middleware/error_handler.go(lines 44, 69, 82, 84, 87, 89)internal/middleware/rate_limiter.go(line 102)internal/middleware/request_tracing.go(lines 48, 58, 60)internal/middleware/security.go(line 14)
Step-by-Step Fixes
Step 1: Fix Deprecated Logger Middleware
File: cmd/server/main.go
Line 142:
// BEFORE:
e.Use(echomiddleware.Logger())
// AFTER:
e.Use(echomiddleware.RequestLogger())
File: internal/router/router.go
Line ~144:
// BEFORE:
e.Use(echomiddleware.Logger())
// AFTER:
e.Use(echomiddleware.RequestLogger())
Step 2: Fix Removed API Methods
File: internal/app/app.go
Lines 26-32 - Initialize server field:
// REPLACE lines 26-32 with:
func New(echo *echo.Echo) *App {
return &App{
echo: echo,
server: nil, // Will be set in StartServer()
shutdownTimeout: 30 * time.Second,
shutdownDone: make(chan struct{}),
}
}
Add StartServer method after New() (after line 32):
// StartServer creates HTTP server and starts listening
func (a *App) StartServer(addr string) error {
a.server = &http.Server{
Addr: addr,
Handler: a.echo,
}
// Start HTTP server in background
go func() {
if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server failed to start: %v", err)
}
}()
return nil
}
Lines 82-94 - Replace goroutine in Shutdown():
// REPLACE lines 82-94 with:
// Stop accepting new connections and shutdown HTTP server
log.Println("Stopping HTTP server...")
if a.server != nil {
ctx, cancel := context.WithTimeout(context.Background(), a.shutdownTimeout)
defer cancel()
if err := a.server.Shutdown(ctx); err != nil {
log.Printf("Error stopping HTTP server: %v", err)
}
}
log.Println("All services stopped")
File: cmd/server/main.go
Lines 197-214 - Replace server startup:
// REPLACE lines 197-214 with:
// ========================================================================
// START SERVER (managed by app lifecycle)
// ========================================================================
log.Printf("Starting server on port %s", cfg.ServerPort)
// Start HTTP server
if err := application.StartServer(":" + cfg.ServerPort); err != nil {
log.Fatalf("Failed to start server: %v", err)
}
// Start application lifecycle (blocks until shutdown signal)
if err := application.Start(); err != nil {
log.Fatalf("Application error: %v", err)
}
Step 3: Fix Middleware Type Signatures
Pattern for Echo v5 Middleware
Echo v5 Middleware Pattern:
func MiddlewareFunc(next echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error { // ← POINTER (required in v5)
// ... middleware logic
return next(c) // ← c is already a pointer, no & needed
}
}
File: internal/middleware/device_auth.go
Line 38:
// BEFORE:
return func(c echo.Context) error {
// AFTER:
return func(c *echo.Context) error {
Line 170:
// BEFORE:
return func(c echo.Context) error {
// AFTER:
return func(c *echo.Context) error {
Line 212:
// BEFORE:
return func(c echo.Context) error {
// AFTER:
return func(c *echo.Context) error {
File: internal/middleware/error_handler.go
Line 44:
// BEFORE:
func RespondWithError(c echo.Context, code int, message string, err error) error {
// AFTER:
func RespondWithError(c *echo.Context, code int, message string, err error) error {
Line 69:
// BEFORE:
func RespondWithHTTPError(c echo.Context, httpErr *HTTPError) error {
// AFTER:
func RespondWithHTTPError(c *echo.Context, httpErr *HTTPError) error {
Line 82:
// BEFORE:
func WrapHandler(fn func(c echo.Context) error) echo.HandlerFunc {
return func(c *echo.Context) error {
err := fn(c)
// AFTER:
func WrapHandler(fn func(*echo.Context) error) echo.HandlerFunc {
return func(c *echo.Context) error {
err := fn(c) // c is already *echo.Context
Lines 84, 87, 89: No change - calls to helpers will now work with updated signatures
File: internal/middleware/rate_limiter.go
Line 102:
// BEFORE:
return func(c echo.Context) error {
// AFTER:
return func(c *echo.Context) error {
File: internal/middleware/request_tracing.go
Line 48:
// BEFORE:
return func(c echo.Context) error {
// AFTER:
return func(c *echo.Context) error {
Lines 57-61:
// BEFORE:
recorder := &responseWriter{
ResponseWriter: c.Response().Writer,
}
c.Response().Writer = recorder
// AFTER:
recorder := &responseWriter{
ResponseWriter: *c.Response(), // Dereference: *echo.Response -> http.ResponseWriter
}
Note: c.Response().Status (line 108) should work - Status field exists in Echo v5
File: internal/middleware/security.go
Line 14:
// BEFORE:
return func(c echo.Context) error {
// AFTER:
return func(c *echo.Context) error {
WebSocket Fix
After Echo v5 migration, the WebSocket handler should now work natively:
File: internal/handlers/websocket.go
The existing code should now work:
ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
This previously failed with "response does not implement http.Hijacker" but Echo v5's Response now supports the rwUnwrapper interface needed for WebSocket upgrades.
Verification
After making all changes:
-
Build the project:
go build ./cmd/server -
Run tests:
go test ./cmd/server/tests/... -v -
Test WebSocket manually:
- Get JWT token:
curl -X POST http://localhost:8765/api/auth/login -H "Content-Type: application/json" -d '{"login":"testuser@tests.bookhoard.internal","password":"Test@Pass123!"}' | jq -r '.access_token' - Connect in browser console:
new WebSocket('ws://localhost:8765/ws/sync?token=YOUR_TOKEN')
- Get JWT token:
Rollback Plan
If Echo v5 migration fails:
-
Revert go.mod changes:
// Change back to v4: github.com/labstack/echo/v4 v4.15.1 github.com/labstack/echo-jwt/v4 v4.4.0 -
Restore import statements in all modified files
-
Revert all code changes