Разработка бэкенда сайта на Go (Echo)

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка бэкенда сайта на Go (Echo)
Средняя
от 1 недели до 3 месяцев
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Разработка бэкенда сайта на Go (Echo)

Echo и Gin решают одну задачу, но по-разному. Echo делает акцент на расширяемости: middleware, context, binder — всё это интерфейсы, которые можно заменить. Gin чуть быстрее в бенчмарках, Echo чуть удобнее в архитектурном плане, особенно при написании middleware. На реальных проектах разница в производительности несущественна — узкое место почти всегда в БД, а не в роутере.

Echo выбирают за: удобный API группировки маршрутов, встроенный Validator interface, хорошую поддержку WebSocket и SSE, читаемый код middleware.

Инициализация и маршруты

// internal/server/server.go
package server

import (
    "net/http"

    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
    "github.com/myapp/internal/domain/product"
    "github.com/myapp/internal/domain/auth"
    custmw "github.com/myapp/internal/middleware"
)

type Server struct {
    echo    *echo.Echo
    product *product.Handler
    auth    *auth.Handler
}

func New(deps Dependencies) *Server {
    e := echo.New()
    e.HideBanner = true
    e.Validator = custmw.NewValidator()

    // Встроенные middleware
    e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
        LogMethod: true, LogURI: true, LogStatus: true, LogLatency: true,
        LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
            deps.Logger.Info("request",
                "method", v.Method, "uri", v.URI,
                "status", v.Status, "latency", v.Latency)
            return nil
        },
    }))
    e.Use(middleware.RecoverWithConfig(middleware.RecoverConfig{
        LogErrorFunc: func(c echo.Context, err error, stack []byte) error {
            deps.Logger.Error("panic recovered", "error", err)
            return nil
        },
    }))
    e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
        AllowOrigins:     deps.Config.AllowedOrigins,
        AllowCredentials: true,
    }))
    e.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(100)))

    s := &Server{echo: e, product: product.NewHandler(deps), auth: auth.NewHandler(deps)}
    s.registerRoutes()
    return s
}

func (s *Server) registerRoutes() {
    api := s.echo.Group("/api/v1")

    // Public
    authGroup := api.Group("/auth")
    authGroup.POST("/login", s.auth.Login)
    authGroup.POST("/refresh", s.auth.Refresh)

    // Protected
    restricted := api.Group("", custmw.JWT(s.cfg.JWTSecret))
    restricted.GET("/profile", s.auth.Profile)

    products := api.Group("/products")
    products.GET("", s.product.List)
    products.GET("/:id", s.product.Get)

    adminProducts := products.Group("", custmw.JWT(s.cfg.JWTSecret), custmw.RequireRole("admin"))
    adminProducts.POST("", s.product.Create)
    adminProducts.PUT("/:id", s.product.Update)
    adminProducts.DELETE("/:id", s.product.Delete)
}

Кастомный Validator

// internal/middleware/validator.go
package middleware

import (
    "github.com/go-playground/validator/v10"
    "github.com/labstack/echo/v4"
    "net/http"
)

type CustomValidator struct {
    v *validator.Validate
}

func NewValidator() *CustomValidator {
    v := validator.New()

    // Кастомный тег для slug
    v.RegisterValidation("slug", func(fl validator.FieldLevel) bool {
        val := fl.Field().String()
        for _, c := range val {
            if !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-') {
                return false
            }
        }
        return len(val) > 0
    })

    return &CustomValidator{v: v}
}

func (cv *CustomValidator) Validate(i interface{}) error {
    if err := cv.v.Struct(i); err != nil {
        errs := err.(validator.ValidationErrors)
        fields := make(map[string]string, len(errs))
        for _, e := range errs {
            fields[e.Field()] = e.Tag()
        }
        return echo.NewHTTPError(http.StatusUnprocessableEntity, fields)
    }
    return nil
}

Handler с Echo Context

// internal/domain/product/handler.go
package product

import (
    "net/http"
    "strconv"

    "github.com/labstack/echo/v4"
)

type Handler struct {
    svc *Service
}

type CreateRequest struct {
    Name        string   `json:"name" validate:"required,min=2,max=255"`
    Price       float64  `json:"price" validate:"required,gt=0"`
    CategoryID  *int     `json:"category_id" validate:"omitempty,gt=0"`
    Tags        []string `json:"tags" validate:"omitempty,max=10,dive,min=1,max=50"`
}

func (h *Handler) Create(c echo.Context) error {
    var req CreateRequest
    if err := c.Bind(&req); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, err.Error())
    }
    if err := c.Validate(&req); err != nil {
        return err // echo.HTTPError с полями
    }

    userID := c.Get("userID").(int)
    product, err := h.svc.Create(c.Request().Context(), req, userID)
    if err != nil {
        return mapServiceError(err)
    }

    return c.JSON(http.StatusCreated, product)
}

func (h *Handler) Get(c echo.Context) error {
    id, err := strconv.Atoi(c.Param("id"))
    if err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, "invalid id")
    }

    product, err := h.svc.GetByID(c.Request().Context(), id)
    if err != nil {
        return mapServiceError(err)
    }

    return c.JSON(http.StatusOK, product)
}

WebSocket и SSE

Echo хорошо работает с реальным временем:

// WebSocket
func (h *NotificationHandler) Subscribe(c echo.Context) error {
    userID := c.Get("userID").(int)

    conn, _, _, err := ws.UpgradeHTTP(c.Request(), c.Response())
    if err != nil {
        return err
    }
    defer conn.Close()

    ch := h.broker.Subscribe(userID)
    defer h.broker.Unsubscribe(userID, ch)

    for msg := range ch {
        if err := wsutil.WriteServerMessage(conn, ws.OpText, msg); err != nil {
            break
        }
    }
    return nil
}

// Server-Sent Events
func (h *EventHandler) Stream(c echo.Context) error {
    c.Response().Header().Set(echo.HeaderContentType, "text/event-stream")
    c.Response().Header().Set("Cache-Control", "no-cache")
    c.Response().Header().Set("Connection", "keep-alive")
    c.Response().WriteHeader(http.StatusOK)

    ticker := time.NewTicker(5 * time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-c.Request().Context().Done():
            return nil
        case t := <-ticker.C:
            fmt.Fprintf(c.Response(), "data: %s\n\n", t.Format(time.RFC3339))
            c.Response().Flush()
        }
    }
}

Кеширование с Redis

// internal/cache/redis.go
type Cache struct {
    client *redis.Client
}

func (c *Cache) GetOrSet(ctx context.Context, key string, ttl time.Duration, fn func() (interface{}, error)) (interface{}, error) {
    cached, err := c.client.Get(ctx, key).Bytes()
    if err == nil {
        var result interface{}
        if err := json.Unmarshal(cached, &result); err == nil {
            return result, nil
        }
    }

    value, err := fn()
    if err != nil {
        return nil, err
    }

    if data, err := json.Marshal(value); err == nil {
        c.client.Set(ctx, key, data, ttl)
    }

    return value, nil
}

// Использование в handler
func (h *Handler) List(c echo.Context) error {
    cacheKey := fmt.Sprintf("products:list:%s", c.QueryString())

    result, err := h.cache.GetOrSet(c.Request().Context(), cacheKey, 5*time.Minute, func() (interface{}, error) {
        return h.svc.List(c.Request().Context(), parseListParams(c))
    })
    if err != nil {
        return echo.NewHTTPError(http.StatusInternalServerError)
    }

    return c.JSON(http.StatusOK, result)
}

Graceful shutdown

func (s *Server) Start(addr string) error {
    go func() {
        if err := s.echo.Start(addr); err != http.ErrServerClosed {
            s.logger.Error("server error", "err", err)
        }
    }()

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    return s.echo.Shutdown(ctx)
}

Сроки разработки

Echo-проекты по структуре аналогичны Gin, разница небольшая:

  • Scaffold + DI + DB — 3–5 дней
  • Routes + handlers + middleware — 1–1,5 недели
  • Services + repositories — 1–3 недели
  • WebSocket/SSE если нужны — 3–5 дней дополнительно
  • Тесты + Docker — 1 неделя

API для сайта: 4–8 недель. Echo и Gin равнозначны по возможностям — выбирайте по предпочтению команды.