Професійна розробка бекенду на Go (Echo)

Бекенд на Go з фреймворком Echo — це вибір для проєктів, де важливі швидкість і надійність під високим навантаженням. Ми розробляємо серверну частину під ключ: від архітектури та middleware до WebSocket-з'єднань та інтеграції з Redis. Наша команда бере на себе весь цикл робіт, забезпечуючи стабільну роботу та подальшу підтримку вашого сервісу.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Професійна розробка бекенду на Go (Echo)
Середній
від 1 тижня до 3 місяців
Часті запитання

Високонавантажений бекенд на Go (Echo)

У production-середовищі Go-сервер повинен утримувати десятки тисяч з'єднань, обробляти запити за мілісекунди та коректно завершуватися при перезавантаженні. Стандартний net/http не надає вбудованого rate limiter, graceful shutdown або зручної маршрутизації. Тому ми обираємо Echo — фреймворк, який не нав'язує архітектуру, але дає всі інструменти для production-рішення. Наша команда з 7+ річним досвідом у Go-розробці реалізувала 50+ проєктів, від простих API до real-time платформ. Як сказано в документації Echo: Echo is a high performance, extensible, minimalist Go web framework.

Чому Echo, а не Gin?

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

Ключові компоненти бекенду на Echo

Ініціалізація та маршрути

// internal/server/server.go
package server

import (
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"context"
	"time"
	"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
	logger  Logger
	cfg     Config
}

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),
		logger:  deps.Logger,
		cfg:     deps.Config,
	}
	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)
}

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

Кастомний 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

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

Як Echo справляється з високим навантаженням?

Механізми Echo — middleware без аллокацій, context з пулом об'єктів, вбудований rate limiter — дають низький TTFB і передбачуване споживання пам'яті. У парі з Redis-кешуванням відповідей ми утримуємо LCP у зеленій зоні Core Web Vitals навіть при 10k RPS. Код вище (GetOrSet) показує патерн cache-aside, який ми застосовуємо на кожному GET-ендпоінті.

Типові помилки та їх вирішення

Серед частих помилок при розробці на Echo — N+1 запити, відсутність graceful shutdown, слабка валідація та ігнорування CORS. N+1 запити виникають при роботі з ORM, коли на кожен елемент колекції виконується окремий запит; ми використовуємо Repository pattern з явними JOIN-запитами та пагінацією. Відсутність graceful shutdown призводить до втрати з'єднань при рестарті — впроваджуємо echo.Shutdown з таймаутом 10 секунд. Слабка валідація вирішується реєстрацією кастомного валідатора з бізнес-правилами (наприклад, slug-формат). Ігнорування CORS легко виправити, налаштувавши middleware з точним списком дозволених джерел.

Як уникнути N+1 запитів?

Проблема N+1 виникає при роботі з ORM, коли на кожен елемент колекції виконується окремий запит. Ми використовуємо Repository pattern з явними JOIN-запитами та пагінацією. Наприклад, для списку товарів з категоріями — один запит з LEFT JOIN на категорії. Додатково включаємо eager loading у сервісах.

Процес та обсяг робіт

Компонент Опис
Архітектура Вибір патерну (Repository, BFF), схема БД, контракти API
Розробка Middleware, handlers, сервіси, інтеграційні тести
Інфраструктура Docker, docker-compose, CI/CD (GitHub Actions)
Документація OpenAPI (Swagger), README з інструкцією по запуску
Деплой Налаштування Nginx, сертифікати, моніторинг (Prometheus/Grafana)
Гарантія 2 тижні підтримки після здачі, фіксація багів за договором
  1. Аналітика — розбираємо поточну архітектуру, вузькі місця, профілюємо.
  2. Проектування — схеми, вибір стеку (PostgreSQL/Redis/Elasticsearch).
  3. Реалізація — пишемо код, покриваємо unit- та e2e-тестами.
  4. Тестування — навантажувальні тести (vegeta/k6), перевірка LCP/CLS/INP.
  5. Деплой — розгортаємо на вашому хостингу (Selectel/Beget/REG.RU) або хмарі.
  6. Передача — повна документація, доступи, навчання команди (опціонально).

Рядки розробки

  • Scaffold + DI + БД — 3–5 днів.
  • Routes + handlers + middleware — 1–1,5 тижні.
  • Services + repositories — 1–3 тижні.
  • WebSocket/SSE — 3–5 днів додатково.
  • Тести + Docker — 1 тиждень.

API для сайту: від 4 до 8 тижнів. Бюджет варіюється залежно від складності. Економія на інфраструктурі при правильному кешуванні може становити до 40% щомісячних витрат. Відсутність graceful shutdown може коштувати бізнесу втрати замовлень на десятки тисяч гривень за годину простою. Отримайте консультацію інженера — оцінимо ваш проєкт за 24 години. Зв'яжіться з нами, щоб обговорити деталі. Замовте оцінку вашого проєкту — ми підготуємо пропозицію протягом дня.

Що включено в гарантію? Після здачі проєкту ми надаємо 2 тижні підтримки, протягом яких виправляємо баги та відповідаємо на питання. При необхідності продовжуємо гарантійний період за окремим договором.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060