Розробка веб-бекенду на Go (Echo)
Echo й Gin вирішують одне завдання, різними підходами. Echo робить акцент на розширюваності: middleware, context, binder—все це інтерфейси, які можна замінити. Gin трішки швидший у бенчмарках, Echo трішки зручніший архітектурно, особливо при написанні middleware. На реальних проектах різниця в продуктивності несуттєва—вузьке місце майже завжди у БД, а не у маршрутизаторі.
Вибирають Echo за: зручний API групування маршрутів, вбудований Validator interface, хорошу підтримку WebSocket й SSE, читаний код middleware.
Ініціалізація й маршрути
package server
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/myapp/internal/domain/product"
custmw "github.com/myapp/internal/middleware"
)
type Server struct {
echo *echo.Echo
product *product.Handler
}
func New(deps Dependencies) *Server {
e := echo.New()
e.HideBanner = true
e.Validator = custmw.NewValidator()
// Вбудований middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(middleware.CORS())
e.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(100)))
s := &Server{echo: e, product: product.NewHandler(deps)}
s.registerRoutes()
return s
}
func (s *Server) registerRoutes() {
api := s.echo.Group("/api/v1")
// Публічні
authGroup := api.Group("/auth")
authGroup.POST("/login", s.product.Login)
// Захищені
restricted := api.Group("", custmw.JWT())
restricted.GET("/profile", s.product.Profile)
// Продукти
products := api.Group("/products")
products.GET("", s.product.List)
products.GET("/:id", s.product.Get)
}
Handler (Контролер)
package product
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/myapp/internal/domain"
)
type Handler struct {
service *Service
}
func (h *Handler) List(c echo.Context) error {
page := c.QueryParamDefault("page", "1")
limit := c.QueryParamDefault("limit", "20")
products, total, err := h.service.List(c.Request().Context(), page, limit)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
return c.JSON(http.StatusOK, map[string]interface{}{
"data": products,
"total": total,
})
}
func (h *Handler) Get(c echo.Context) error {
id := c.Param("id")
product, err := h.service.GetByID(c.Request().Context(), id)
if err != nil {
if err == domain.ErrNotFound {
return c.JSON(http.StatusNotFound, map[string]string{"error": "Not found"})
}
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
return c.JSON(http.StatusOK, product)
}
Service (Бізнес-логіка)
package product
import (
"context"
"github.com/myapp/internal/domain"
)
type Service struct {
repo domain.ProductRepository
}
func (s *Service) List(ctx context.Context, page, limit string) ([]domain.Product, int, error) {
products, err := s.repo.FindMany(ctx)
if err != nil {
return nil, 0, err
}
return products, len(products), nil
}
func (s *Service) GetByID(ctx context.Context, id string) (*domain.Product, error) {
return s.repo.FindByID(ctx, id)
}
Шар бази даних
package repository
import (
"context"
"database/sql"
"github.com/myapp/internal/domain"
)
type ProductRepository struct {
db *sql.DB
}
func (r *ProductRepository) FindByID(ctx context.Context, id string) (*domain.Product, error) {
var p domain.Product
err := r.db.QueryRowContext(
ctx,
"SELECT id, name, price, created_at FROM products WHERE id = $1",
id,
).Scan(&p.ID, &p.Name, &p.Price, &p.CreatedAt)
if err == sql.ErrNoRows {
return nil, domain.ErrNotFound
}
return &p, err
}
Кастомний middleware
package middleware
import (
"github.com/labstack/echo/v4"
"github.com/golang-jwt/jwt/v5"
)
func JWT() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
token := c.Request().Header.Get("Authorization")
if token == "" {
return c.JSON(401, map[string]string{"error": "Unauthorized"})
}
// Валідуємо token
_, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
return []byte("secret"), nil
})
if err != nil {
return c.JSON(401, map[string]string{"error": "Invalid token"})
}
return next(c)
}
}
}
Таймлайн
Базовий сетап: маршрути, обробники, middleware—1 день. Інтеграція БД, бізнес-логіка—2–3 дні. Повний API з тестами—1 тиждень.
Go/Echo переваги: скомпільований бінарник, одна розгортка, відмінна продуктивність, статична типізація ловить помилки на ранній стадії.







