GinJuniorCoding
Как реализовать версионирование API (например, /api/v1/, /api/v2/) с помощью групп маршрутов Gin?
Используйте r.Group("/api/v1") и r.Group("/api/v2") — каждая группа получает свой префикс и может иметь собственные middleware и handler-ы.
Версионирование API через группы маршрутов в Gin
Gin предоставляет метод Group(), который возвращает объект *gin.RouterGroup с общим префиксом пути и набором middleware. Это стандартный способ разграничить версии API без дублирования логики.
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// v1 — стабильная версия
v1 := r.Group("/api/v1")
{
v1.GET("/users", listUsersV1)
v1.POST("/users", createUserV1)
v1.GET("/users/:id", getUserV1)
}
// v2 — новая версия с другой структурой ответа
v2 := r.Group("/api/v2")
{
v2.GET("/users", listUsersV2)
v2.POST("/users", createUserV2)
v2.GET("/users/:id", getUserV2)
}
r.Run(":8080")
}
func listUsersV1(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"data": []string{"alice", "bob"}})
}
func listUsersV2(c *gin.Context) {
// v2 возвращает пагинацию и метаданные
c.JSON(http.StatusOK, gin.H{
"data": []string{"alice", "bob"},
"meta": gin.H{"total": 2, "page": 1},
})
}
func createUserV1(c *gin.Context) { c.Status(http.StatusCreated) }
func getUserV1(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"id": c.Param("id")}) }
func createUserV2(c *gin.Context) { c.Status(http.StatusCreated) }
func getUserV2(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"id": c.Param("id")}) }
Группам можно добавлять свои middleware — например, аутентификацию только для v2:
v2 := r.Group("/api/v2", AuthMiddleware())
Для крупных проектов удобно разносить версии по отдельным пакетам и регистрировать их через функцию RegisterRoutes:
// v1/routes.go
package v1
import "github.com/gin-gonic/gin"
func RegisterRoutes(rg *gin.RouterGroup) {
rg.GET("/users", ListUsers)
rg.POST("/users", CreateUser)
}
// main.go
v1Group := r.Group("/api/v1")
v1.RegisterRoutes(v1Group)
v2Group := r.Group("/api/v2")
v2.RegisterRoutes(v2Group)
Такой подход позволяет каждой версии иметь собственные типы запросов/ответов, handler-ы и middleware, не затрагивая другие версии.
Подводные камни
- Фигурные скобки
{ }вокруг маршрутов группы — синтаксический сахар Go, они не создают отдельного scope для переменной группы; это только читаемость. - Если несколько групп используют один и тот же middleware (например, логирование), лучше навесить его на корневой роутер, а не дублировать в каждой группе.
- Prefix
/api/v1не добавляет завершающий слэш — маршрут/usersдаст путь/api/v1/users, а не/api/v1//users; следите за двойными слэшами при конкатенации. - Gin не поддерживает negotiation через заголовок
Accept-Versionиз коробки — если нужна версионность через заголовки, реализуйте отдельный middleware. - Группы не изолируют глобальные middleware, добавленные через
r.Use()до создания группы — они применятся ко всем группам. - При большом количестве версий разрастается время компиляции роутинг-дерева; Gin использует radix tree, поэтому N версий с M маршрутами каждая = N*M узлов.
- Удаление версии из кода не гарантирует возврата 404 клиентам — убедитесь, что nginx или API gateway не кешируют старые маршруты.
Common mistakes
- Давать ответ про версионирование API через группы маршрутов в Gin только на уровне определения, не показывая поведение в реальном приложении.
- Игнорировать границы ответственности вокруг темы «версионирование API через группы маршрутов в Gin»: кто отменяет работу, кто владеет ресурсом и где формируется ответ клиенту.
- Не связывать версионирование API через группы маршрутов в Gin с observability, тестированием или безопасностью, когда это влияет на продакшен-поведение.
What the interviewer is testing
- Точно объясняет, что именно делает версионирование API через группы маршрутов в Gin и где это используется в Go-коде.
- Связывает версионирование API через группы маршрутов в Gin с корректным lifecycle запроса, отменой, конкурентностью или конфигурацией сервера там, где это уместно.
- Не изобретает API и опирается на реальные контракты официальной документации.