Agregar Swagger a tu API de Go

Generación automática de documentación OpenAPI desde anotaciones del código

Índice

La documentación de la API es crucial para cualquier aplicación moderna, y para las APIs de Go con Swagger (OpenAPI) se ha convertido en el estándar de la industria. Para los desarrolladores de Go, swaggo ofrece una solución elegante para generar una documentación de la API exhaustiva directamente desde las anotaciones del código.

especificaciones de swagger api en tablero ágil Esta buena imagen es generada por el modelo de IA Flux 1 dev.

Por qué Swagger es importante para las APIs de Go

Al construir APIs REST, la documentación a menudo se desactualiza a medida que el código evoluciona. Swagger resuelve esto generando la documentación desde su código fuente, asegurando que se mantenga sincronizada con su implementación. La interfaz interactiva de Swagger permite a los desarrolladores probar los puntos de conexión directamente desde el navegador, mejorando significativamente la experiencia del desarrollador.

Para los equipos que construyen microservicios o APIs públicas, la documentación de Swagger se vuelve esencial para:

  • Generación de clientes: Crear automáticamente bibliotecas de cliente en múltiples lenguajes
  • Pruebas de contrato: Validar solicitudes y respuestas contra esquemas definidos
  • Colaboración en equipo: Proporcionar una única fuente de verdad para los contratos de la API
  • Onboarding de desarrolladores: Los nuevos miembros del equipo pueden explorar las APIs de forma interactiva

Primeros pasos con swaggo

La biblioteca swaggo es la herramienta más popular para añadir soporte de Swagger a las aplicaciones de Go. Funciona analizando comentarios especiales en su código y generando archivos de especificación OpenAPI 3.0.

Instalación

Primero, instale la herramienta CLI de swag:

go install github.com/swaggo/swag/cmd/swag@latest

Luego añada el paquete de middleware de Swagger apropiado para su marco de trabajo. Para Gin:

go get -u github.com/swaggo/gin-swagger
go get -u github.com/swaggo/files

Para Echo:

go get -u github.com/swaggo/echo-swagger

Para Fiber:

go get -u github.com/gofiber/swagger

Configuración básica

Comience añadiendo información general de la API en su archivo main.go. De manera similar a cómo estructuraría una API REST en Go, las anotaciones deben ser claras y descriptivas:

// @title           Product API
// @version         1.0
// @description     A product management API with Swagger documentation
// @termsOfService  http://swagger.io/terms/

// @contact.name   API Support
// @contact.url    http://www.swagger.io/support
// @contact.email  support@swagger.io

// @license.name  Apache 2.0
// @license.url   http://www.apache.org/licenses/LICENSE-2.0.html

// @host      localhost:8080
// @BasePath  /api/v1

// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Type "Bearer" followed by a space and JWT token.

func main() {
    // Your application code
}

Implementación con el marco Gin

Implementemos un ejemplo completo usando Gin. Primero, defina sus modelos de datos con etiquetas de estructura:```go type Product struct { ID int json:"id" example:"1" Name string json:"name" example:"Laptop" binding:"required" Description string json:"description" example:"High-performance laptop" Price float64 json:"price" example:"999.99" binding:"required,gt=0" Stock int json:"stock" example:"50" }

type ErrorResponse struct { Error string json:"error" example:"Invalid input" Message string json:"message" example:"Product name is required" }


Ahora anote sus funciones manejadoras. Al trabajar con [operaciones de base de datos](https://www.glukhov.org/es/app-architecture/data-access/comparing-go-orms-gorm-ent-bun-sqlc/ "Comparación de ORMs de Go para PostgreSQL: GORM vs Ent vs Bun vs sqlc - con ejemplos de código"), estas anotaciones ayudan a documentar el flujo de datos:

```go
// GetProduct godoc
// @Summary      Get product by ID
// @Description  Retrieve a single product by its unique identifier
// @Tags         products
// @Accept       json
// @Produce      json
// @Param        id   path      int  true  "Product ID"
// @Success      200  {object}  Product
// @Failure      400  {object}  ErrorResponse
// @Failure      404  {object}  ErrorResponse
// @Router       /products/{id} [get]
func GetProduct(c *gin.Context) {
    id := c.Param("id")
    // Implementation here
    c.JSON(200, Product{ID: 1, Name: "Laptop", Price: 999.99})
}

// CreateProduct godoc
// @Summary      Create a new product
// @Description  Add a new product to the catalog
// @Tags         products
// @Accept       json
// @Produce      json
// @Param        product  body      Product  true  "Product object"
// @Success      201      {object}  Product
// @Failure      400      {object}  ErrorResponse
// @Security     Bearer
// @Router       /products [post]
func CreateProduct(c *gin.Context) {
    var product Product
    if err := c.ShouldBindJSON(&product); err != nil {
        c.JSON(400, ErrorResponse{Error: "Bad Request", Message: err.Error()})
        return
    }
    // Save to database
    c.JSON(201, product)
}

Generación de la documentación

Después de anotar su código, genere la documentación de Swagger:

swag init

Esto crea una carpeta docs con swagger.json, swagger.yaml y archivos de Go. Importe y registre el punto de conexión de Swagger:

package main

import (
    "github.com/gin-gonic/gin"
    swaggerFiles "github.com/swaggo/files"
    ginSwagger "github.com/swaggo/gin-swagger"
    
    _ "yourproject/docs" // Import generated docs
)

func main() {
    r := gin.Default()
    
    // API routes
    v1 := r.Group("/api/v1")
    {
        v1.GET("/products/:id", GetProduct)
        v1.POST("/products", CreateProduct)
    }
    
    // Swagger endpoint
    r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
    
    r.Run(":8080")
}

Ahora acceda a su documentación de la API interactiva en http://localhost:8080/swagger/index.html.

Implementación con el marco Echo

Los usuarios de Echo siguen un patrón similar pero con el middleware específico de Echo:

package main

import (
    "github.com/labstack/echo/v4"
    echoSwagger "github.com/swaggo/echo-swagger"
    
    _ "yourproject/docs"
)

func main() {
    e := echo.New()
    
    // API routes
    api := e.Group("/api/v1")
    api.GET("/products/:id", getProduct)
    api.POST("/products", createProduct)
    
    // Swagger endpoint
    e.GET("/swagger/*", echoSwagger.WrapHandler)
    
    e.Start(":8080")
}

Implementación con el marco Fiber

La implementación de Fiber es igualmente sencilla:

package main

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/swagger"
    
    _ "yourproject/docs"
)

func main() {
    app := fiber.New()
    
    // API routes
    api := app.Group("/api/v1")
    api.Get("/products/:id", getProduct)
    api.Post("/products", createProduct)
    
    // Swagger endpoint
    app.Get("/swagger/*", swagger.HandlerDefault)
    
    app.Listen(":8080")
}

Anotaciones avanzadas de Swagger

Documentación de cuerpos de solicitud complejos

Para estructuras anidadas o matrices:

type CreateOrderRequest struct {
    CustomerID int           `json:"customer_id" example:"123" binding:"required"`
    Items      []OrderItem   `json:"items" binding:"required,min=1"`
    ShippingAddress Address  `json:"shipping_address" binding:"required"`
}

type OrderItem struct {
    ProductID int `json:"product_id" example:"1" binding:"required"`
    Quantity  int `json:"quantity" example:"2" binding:"required,min=1"`
}

type Address struct {
    Street  string `json:"street" example:"123 Main St" binding:"required"`
    City    string `json:"city" example:"New York" binding:"required"`
    ZipCode string `json:"zip_code" example:"10001" binding:"required"`
}

// CreateOrder godoc
// @Summary      Create a new order
// @Description  Create an order with multiple items and shipping information
// @Tags         orders
// @Accept       json
// @Produce      json
// @Param        order  body      CreateOrderRequest  true  "Order details"
// @Success      201    {object}  Order
// @Failure      400    {object}  ErrorResponse
// @Failure      422    {object}  ErrorResponse
// @Security     Bearer
// @Router       /orders [post]
func CreateOrder(c *gin.Context) {
    // Implementation
}

Documentación de cargas de archivos

// UploadImage godoc
// @Summary      Upload product image
// @Description  Upload an image file for a product
// @Tags         products
// @Accept       multipart/form-data
// @Produce      json
// @Param        id    path      int   true  "Product ID"
// @Param        file  formData  file  true  "Image file"
// @Success      200   {object}  map[string]string
// @Failure      400   {object}  ErrorResponse
// @Security     Bearer
// @Router       /products/{id}/image [post]
func UploadImage(c *gin.Context) {
    file, _ := c.FormFile("file")
    // Handle upload
}

Parámetros de consulta y paginación

// ListProducts godoc
// @Summary      List products with pagination
// @Description  Get paginated list of products with optional filtering
// @Tags         products
// @Accept       json
// @Produce      json
// @Param        page      query     int     false  "Page number" default(1)
// @Param        page_size query     int     false  "Items per page" default(10)
// @Param        category  query     string  false  "Filter by category"
// @Param        min_price query     number  false  "Minimum price"
// @Param        max_price query     number  false  "Maximum price"
// @Success      200       {array}   Product
// @Failure      400       {object}  ErrorResponse
// @Router       /products [get]
func ListProducts(c *gin.Context) {
    // Implementation with pagination
}

Autenticación y seguridad

Documente diferentes métodos de autenticación en su API. Para las aplicaciones multi-inquilino, la documentación de autenticación adecuada es crucial:

Autenticación con Token Bearer

// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Type "Bearer" followed by a space and JWT token.

Autenticación con clave de API

// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
// @description API key for authentication

Autenticación OAuth2

// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information

Autenticación básica

// @securityDefinitions.basic BasicAuth

Aplique la seguridad a puntos de conexión específicos:

// @Security Bearer
// @Security ApiKeyAuth

Personalización de la interfaz de Swagger

Puede personalizar la apariencia y el comportamiento de la interfaz de Swagger:

// Custom configuration
url := ginSwagger.URL("http://localhost:8080/swagger/doc.json")
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))

// With custom title
r.GET("/swagger/*any", ginSwagger.WrapHandler(
    swaggerFiles.Handler,
    ginSwagger.URL("http://localhost:8080/swagger/doc.json"),
    ginSwagger.DefaultModelsExpandDepth(-1),
))

Para deshabilitar Swagger en producción:

if os.Getenv("ENV") != "production" {
    r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}

Integración con CI/CD

Automatice la generación de la documentación de Swagger en su pipeline de CI/CD:

# GitHub Actions example
name: Generate Swagger Docs
on: [push]

jobs:
  swagger:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Go
        uses: actions/setup-go@v4
        with:
          go-version: '1.21'
      
      - name: Install swag
        run: go install github.com/swaggo/swag/cmd/swag@latest
      
      - name: Generate Swagger docs
        run: swag init
      
      - name: Commit docs
        run: |
          git config user.name github-actions
          git config user.email github-actions@github.com
          git add docs/
          git commit -m "Update Swagger documentation" || exit 0
          git push

Mejores prácticas

1. Estilo de anotación consistente

Mantenga un formato consistente en todos los puntos de conexión:

// HandlerName godoc
// @Summary      Brief description (under 50 chars)
// @Description  Detailed description of what the endpoint does
// @Tags         resource-name
// @Accept       json
// @Produce      json
// @Param        name  location  type  required  "description"
// @Success      200   {object}  ResponseType
// @Failure      400   {object}  ErrorResponse
// @Router       /path [method]

2. Utilizar ejemplos descriptivos

Añada ejemplos realistas para ayudar a los consumidores de la API:

type User struct {
    ID        int       `json:"id" example:"1"`
    Email     string    `json:"email" example:"user@example.com"`
    CreatedAt time.Time `json:"created_at" example:"2025-01-15T10:30:00Z"`
}

3. Documentar todos los códigos de respuesta

Incluya todos los posibles códigos de estado HTTP:

// @Success      200  {object}  Product
// @Success      201  {object}  Product
// @Failure      400  {object}  ErrorResponse "Bad Request"
// @Failure      401  {object}  ErrorResponse "Unauthorized"
// @Failure      403  {object}  ErrorResponse "Forbidden"
// @Failure      404  {object}  ErrorResponse "Not Found"
// @Failure      422  {object}  ErrorResponse "Validation Error"
// @Failure      500  {object}  ErrorResponse "Internal Server Error"

4. Versionar su API

Utilice la numeración de versiones adecuada en la ruta base:

// @BasePath  /api/v1

Y organice su código en consecuencia:

v1 := r.Group("/api/v1")
v2 := r.Group("/api/v2")

5. Agrupar puntos de conexión relacionados

Use etiquetas para organizar los puntos de conexión lógicamente:

// @Tags products
// @Tags orders
// @Tags users

6. Mantener la documentación actualizada

Ejecute swag init antes de cada commit o intégralo en su proceso de compilación:

#!/bin/bash
# pre-commit hook
swag init
git add docs/

Pruebas de la documentación de Swagger

Al trabajar con arquitecturas serverless como AWS Lambda, probar su documentación de la API se vuelve aún más importante:

func TestSwaggerGeneration(t *testing.T) {
    // Verify swagger.json exists
    _, err := os.Stat("./docs/swagger.json")
    if err != nil {
        t.Fatal("swagger.json not found, run 'swag init'")
    }
    
    // Verify swagger endpoint responds
    r := setupRouter()
    w := httptest.NewRecorder()
    req, _ := http.NewRequest("GET", "/swagger/index.html", nil)
    r.ServeHTTP(w, req)
    
    assert.Equal(t, 200, w.Code)
}

Problemas comunes y soluciones

La documentación no se actualiza

Si los cambios no aparecen, asegúrese de que está regenerando la documentación:

swag init --parseDependency --parseInternal

La bandera --parseDependency analiza dependencias externas, y --parseInternal analiza paquetes internos.

Tipos personalizados no reconocidos

Para tipos de paquetes externos, use la etiqueta swaggertype:

type CustomTime struct {
    time.Time
}

func (CustomTime) SwaggerDoc() map[string]string {
    return map[string]string{
        "time": "RFC3339 timestamp",
    }
}

O use la etiqueta swaggertype:

type Product struct {
    ID        int        `json:"id"`
    UpdatedAt CustomTime `json:"updated_at" swaggertype:"string" format:"date-time"`
}

Matrices y enumeraciones

Documente los tipos de matriz y las enumeraciones:

type Filter struct {
    Status []string `json:"status" enums:"active,inactive,pending"`
    Tags   []string `json:"tags"`
}

// @Param  status  query  string  false  "Status filter" Enums(active, inactive, pending)

Enfoques alternativos

Si bien swaggo es la opción más popular, existen otras opciones:

go-swagger

Una alternativa más completa pero compleja:

brew install go-swagger
swagger generate spec -o ./swagger.json

Archivos OpenAPI manuales

Para un control total, escriba especificaciones OpenAPI manualmente en YAML:

openapi: 3.0.0
info:
  title: Product API
  version: 1.0.0
paths:
  /products:
    get:
      summary: List products
      responses:
        '200':
          description: Success

Y sírvala con:

r.StaticFile("/openapi.yaml", "./openapi.yaml")

Integración con IA y LLMs

Al construir APIs que se integran con servicios de IA, la documentación adecuada se vuelve crucial. Por ejemplo, al trabajar con salidas estructuradas de LLMs, Swagger ayuda a documentar esquemas de solicitud y respuesta complejos:

type LLMRequest struct {
    Prompt      string            `json:"prompt" example:"Summarize this text"`
    Model       string            `json:"model" example:"qwen2.5:latest"`
    Temperature float64           `json:"temperature" example:"0.7" minimum:"0" maximum:"2"`
    MaxTokens   int               `json:"max_tokens" example:"1000" minimum:"1"`
    Schema      map[string]interface{} `json:"schema,omitempty"`
}

// GenerateStructured godoc
// @Summary      Generate structured LLM output
// @Description  Generate text with constrained output schema
// @Tags         llm
// @Accept       json
// @Produce      json
// @Param        request  body      LLMRequest  true  "LLM parameters"
// @Success      200      {object}  map[string]interface{}
// @Failure      400      {object}  ErrorResponse
// @Router       /llm/generate [post]
func GenerateStructured(c *gin.Context) {
    // Implementation
}

Consideraciones de rendimiento

La documentación de Swagger tiene un impacto de rendimiento mínimo:

  • Tiempo de compilación: swag init toma de 1 a 3 segundos para la mayoría de los proyectos
  • Tiempo de ejecución: La documentación se carga una vez al inicio
  • Memoria: Típicamente añade de 1 a 2 MB al tamaño del binario
  • Tiempo de respuesta: Sin impacto en los propios puntos de conexión de la API

Para APIs muy grandes (100+ puntos de conexión), considere:

  • Dividir en múltiples archivos de Swagger
  • Carga diferida de los activos de la interfaz de Swagger
  • Servir la documentación desde un servicio separado

Consideraciones de seguridad

Al exponer la documentación de Swagger:

  1. Deshabilitar en Producción (si la API es interna):
if os.Getenv("ENV") == "production" {
    // Don't register Swagger endpoint
    return
}
  1. Añadir Autenticación:
authorized := r.Group("/swagger")
authorized.Use(AuthMiddleware())
authorized.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
  1. Limitar la tasa del punto de conexión:
r.GET("/swagger/*any", RateLimitMiddleware(), ginSwagger.WrapHandler(swaggerFiles.Handler))
  1. Nunca exponer detalles internos:
  • No documente puntos de conexión internos
  • Evite exponer esquemas de base de datos directamente
  • Sanitice los mensajes de error en la documentación

Conclusión

Añadir documentación de Swagger a su API de Go transforma la experiencia del desarrollador de un proceso de conjeturas a una exploración guiada. La biblioteca swaggo hace este proceso sencillo generando documentación OpenAPI exhaustiva desde sus anotaciones de código.

Puntos clave:

  • Comience con anotaciones básicas y amplíe gradualmente
  • Mantenga la documentación sincronizada con el código a través de CI/CD
  • Utilice la interfaz de Swagger para pruebas interactivas durante el desarrollo
  • Documente a fondo la autenticación, los errores y los casos extremos
  • Considere las implicaciones de seguridad al exponer la documentación

Ya sea que esté construyendo microservicios, APIs públicas o herramientas internas, la documentación de Swagger da frutos en la reducción de la carga de soporte, el onboarding más rápido y un mejor diseño de la API. La inversión inicial en aprender la sintaxis de anotación se convierte rápidamente en una rutina, y la generación automatizada asegura que su documentación nunca se quede atrás con su implementación.

Para los desarrolladores de Go, la combinación de tipado fuerte, generación de código y el sistema de anotaciones de swaggo crea un flujo de trabajo poderoso que convierte la documentación de la API en una parte natural del proceso de desarrollo en lugar de una última consideración.

Si la pila de HTTP subyacente aún es una pregunta abierta, Marcos de trabajo web de Go en 2026: net/http, chi, Gin, Echo, Fiber comparados compara las cinco opciones con la misma carga de trabajo, por lo que la decisión del marco de trabajo puede tomarse antes de integrar las herramientas de documentación.

Enlaces útiles

Recursos externos

Suscribirse

Recibe nuevas publicaciones sobre sistemas, infraestructura e ingeniería de IA.