Dodanie Swagger do Twojego API w Go
Automatyczne generowanie dokumentacji OpenAPI z adnotacji kodu
Dokumentacja API jest kluczowa dla każdego nowoczesnego oprogramowania, a dla API w Go Swagger (OpenAPI) stał się standardem branżowym. Dla deweloperów Go biblioteka swaggo oferuje eleganckie rozwiązanie do generowania kompleksowej dokumentacji API bezpośrednio z adnotacji w kodzie.
To ładne zdjęcie zostało wygenerowane przez model AI Flux 1 dev.
Dlaczego Swagger jest ważny dla API w Go
Przy budowaniu API REST dokumentacja często staje się nieaktualna wraz z ewolucją kodu. Swagger rozwiązuje ten problem, generując dokumentację bezpośrednio z kodu źródłowego, co zapewnia jej synchronizację z implementacją. Interaktywny interfejs Swagger UI pozwala deweloperom testować punkty końcowe (endpoints) bezpośrednio z przeglądarki, co znacząco poprawia doświadczenie deweloperskie.
Dla zespołów budujących mikrousługi lub publiczne API dokumentacja Swagger staje się niezbędna w zakresie:
- Generowania klientów: Automatyczne tworzenie bibliotek klienckich w wielu językach
- Testowania kontraktowego: Walidacja żądań i odpowiedzi zgodnie z zdefiniowanymi schematami
- Współpracy zespołowej: Zapewnienie pojedynczego źródła prawdy dla kontraktów API
- Onboardingu deweloperów: Nowi członkowie zespołu mogą eksplorować API w sposób interaktywny
Rozpoczęcie pracy z swaggo
Biblioteka swaggo to najpopularniejsze narzędzie do dodawania wsparcia dla Swagger w aplikacjach Go. Działa ona poprzez analizę specjalnych komentarzy w Twoim kodzie i generowanie plików ze specyfikacją OpenAPI 3.0.
Instalacja
Najpierw zainstaluj narzędzie CLI swag:
go install github.com/swaggo/swag/cmd/swag@latest
Następnie dodaj odpowiedni pakiet middleware Swagger dla swojego frameworka. Dla Gin:
go get -u github.com/swaggo/gin-swagger
go get -u github.com/swaggo/files
Dla Echo:
go get -u github.com/swaggo/echo-swagger
Dla Fiber:
go get -u github.com/gofiber/swagger
Podstawowa konfiguracja
Rozpocznij od dodania ogólnych informacji o API w pliku main.go. Podobnie jak to, w jaki sposób strukturyzujesz API REST w Go, adnotacje powinny być przejrzyste i opisowe:
// @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() {
// Twój kod aplikacji
}
Implementacja z frameworkiem Gin
Zaimplementujmy kompletny przykład, używając Gin. Najpierw zdefiniuj swoje modele danych z tagami struktury:
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"`
}
Teraz dodaj adnotacje do funkcji obsługujących żądania (handlerów). Przy pracy z operacjami na bazie danych, te adnotacje pomagają w udokumentowaniu przepływu danych:
// 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")
// Implementacja tutaj
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
}
// Zapis do bazy danych
c.JSON(201, product)
}
Generowanie dokumentacji
Po adnotowaniu kodu wygeneruj dokumentację Swagger:
swag init
Działa to tworzy folder docs zawierający swagger.json, swagger.yaml oraz pliki Go. Zaimportuj i zarejestruj punkt końcowy Swagger:
package main
import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
_ "yourproject/docs" // Import wygenerowanej dokumentacji
)
func main() {
r := gin.Default()
// Trasy API
v1 := r.Group("/api/v1")
{
v1.GET("/products/:id", GetProduct)
v1.POST("/products", CreateProduct)
}
// Punkt końcowy Swagger
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.Run(":8080")
}
Teraz uzyskaj dostęp do swojej interaktywnej dokumentacji API pod adresem http://localhost:8080/swagger/index.html.
Implementacja z frameworkiem Echo
Użytkownicy Echo stosują podobny wzorzec, ale z middleware specyficznym dla Echo:
package main
import (
"github.com/labstack/echo/v4"
echoSwagger "github.com/swaggo/echo-swagger"
_ "yourproject/docs"
)
func main() {
e := echo.New()
// Trasy API
api := e.Group("/api/v1")
api.GET("/products/:id", getProduct)
api.POST("/products", createProduct)
// Punkt końcowy Swagger
e.GET("/swagger/*", echoSwagger.WrapHandler)
e.Start(":8080")
}
Implementacja z frameworkiem Fiber
Implementacja z użyciem Fiber jest równie prosta:
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/swagger"
_ "yourproject/docs"
)
func main() {
app := fiber.New()
// Trasy API
api := app.Group("/api/v1")
api.Get("/products/:id", getProduct)
api.Post("/products", createProduct)
// Punkt końcowy Swagger
app.Get("/swagger/*", swagger.HandlerDefault)
app.Listen(":8080")
}
Zaawansowane adnotacje Swagger
Dokumentowanie złożonych ciał żądań
Dla struktur zagnieżdżonych lub tablic:
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) {
// Implementacja
}
Dokumentowanie wysyłania plików
// 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")
// Obsługa wysyłki
}
Parametry zapytania i paginacja
// 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) {
// Implementacja z paginacją
}
Uwierzytelnianie i bezpieczeństwo
Dokumentuj różne metody uwierzytelniania w swoim API. W przypadku aplikacji wielodonajemczych (multi-tenant), właściwa dokumentacja uwierzytelniania jest kluczowa:
Autoryzacja tokenem Bearer
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Type "Bearer" followed by a space and JWT token.
Autoryzacja kluczem API
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
// @description API key for authentication
Autoryzacja 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
Autoryzacja podstawowa (Basic Authentication)
// @securityDefinitions.basic BasicAuth
Stosuj zabezpieczenia do konkretnych punktów końcowych:
// @Security Bearer
// @Security ApiKeyAuth
Dostosowywanie interfejsu Swagger UI
Możesz dostosować wygląd i zachowanie interfejsu Swagger UI:
// Niestandardowa konfiguracja
url := ginSwagger.URL("http://localhost:8080/swagger/doc.json")
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))
// Z niestandardowym tytułem
r.GET("/swagger/*any", ginSwagger.WrapHandler(
swaggerFiles.Handler,
ginSwagger.URL("http://localhost:8080/swagger/doc.json"),
ginSwagger.DefaultModelsExpandDepth(-1),
))
Aby wyłączyć Swagger w środowisku produkcyjnym:
if os.Getenv("ENV") != "production" {
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
Integracja z CI/CD
Zautomatyzuj generowanie dokumentacji Swagger w swoim pipeline CI/CD:
# Przykład dla GitHub Actions
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
Najlepsze praktyki
1. Spójny styl adnotacji
Zachowuj spójne formatowanie dla wszystkich punktów końcowych:
// 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. Stosuj opisowe przykłady
Dodawaj realistyczne przykłady, aby pomóc konsumentom 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. Dokumentuj wszystkie kody odpowiedzi
Włącz wszystkie możliwe kody statusu 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. Wersjonuj swoje API
Stosuj właściwe wersjonowanie w ścieżce bazowej:
// @BasePath /api/v1
I organizuj kod odpowiednio:
v1 := r.Group("/api/v1")
v2 := r.Group("/api/v2")
5. Grupuj powiązane punkty końcowe
Używaj tagów, aby logicznie uporządkować punkty końcowe:
// @Tags products
// @Tags orders
// @Tags users
6. Utrzymuj dokumentację w aktualności
Uruchamiaj swag init przed każdym commitem lub zintegruj ten proces z etapem budowania:
#!/bin/bash
# pre-commit hook
swag init
git add docs/
Testowanie dokumentacji Swagger
Przy pracy z architekturami bezserwerowymi, takimi jak AWS Lambda, testowanie dokumentacji API staje się jeszcze ważniejsze:
func TestSwaggerGeneration(t *testing.T) {
// Sprawdź, czy swagger.json istnieje
_, err := os.Stat("./docs/swagger.json")
if err != nil {
t.Fatal("swagger.json not found, run 'swag init'")
}
// Sprawdź, czy punkt końcowy swagger odpowiada
r := setupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/swagger/index.html", nil)
r.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
}
Częste problemy i rozwiązania
Dokumentacja się nie aktualizuje
Jeśli zmiany nie pojawiają się w dokumentacji, upewnij się, że regenerujesz ją poprawnie:
swag init --parseDependency --parseInternal
Flaga --parseDependency analizuje zewnętrzne zależności, a --parseInternal analizuje wewnętrzne pakiety.
Nierozpoznawane typy niestandardowe
Dla typów z zewnętrznych pakietów użyj tagu swaggertype:
type CustomTime struct {
time.Time
}
func (CustomTime) SwaggerDoc() map[string]string {
return map[string]string{
"time": "RFC3339 timestamp",
}
}
Albo użyj tagu swaggertype:
type Product struct {
ID int `json:"id"`
UpdatedAt CustomTime `json:"updated_at" swaggertype:"string" format:"date-time"`
}
Tablice i wyliczenia (Enums)
Dokumentuj typy tablicowe i wyliczenia:
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)
Alternatywne podejścia
Chociaż swaggo jest najpopularniejszym wyborem, istnieją inne opcje:
go-swagger
Bogatsze w funkcje, ale bardziej złożone rozwiązanie:
brew install go-swagger
swagger generate spec -o ./swagger.json
Ręczne pliki OpenAPI
Dla pełnej kontroli, pisz specyfikacje OpenAPI ręcznie w YAML:
openapi: 3.0.0
info:
title: Product API
version: 1.0.0
paths:
/products:
get:
summary: List products
responses:
'200':
description: Success
Następnie serwuj je za pomocą:
r.StaticFile("/openapi.yaml", "./openapi.yaml")
Integracja z AI i LLM
Przy budowaniu API integrujących się z usługami AI, właściwa dokumentacja staje się kluczowa. Na przykład, przy pracy z strukturalizowanymi wyjściami LLM, Swagger pomaga w dokumentowaniu złożonych schematów żądań i odpowiedzi:
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) {
// Implementacja
}
Rozważania dotyczące wydajności
Dokumentacja Swagger ma minimalny wpływ na wydajność:
- Czas budowania:
swag initzajmuje 1-3 sekundy dla większości projektów - Czas uruchamiania: Dokumentacja jest ładowana raz przy starcie
- Pamięć: Zwykle dodaje 1-2 MB do rozmiaru binarystyki
- Czas odpowiedzi: Brak wpływu na same punkty końcowe API
Dla bardzo dużych API (100+ punktów końcowych), rozważ:
- Podział na wiele plików Swagger
- Leniwe ładowanie zasobów interfejsu Swagger UI
- Serwowanie dokumentacji z oddzielnej usługi
Rozważania dotyczące bezpieczeństwa
Przy udostępnianiu dokumentacji Swagger:
- Wyłącz w produkcji (jeśli API jest wewnętrzne):
if os.Getenv("ENV") == "production" {
// Nie rejestruj punktu końcowego Swagger
return
}
- Dodaj uwierzytelnianie:
authorized := r.Group("/swagger")
authorized.Use(AuthMiddleware())
authorized.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
- Ogranicz przepustowość punktu końcowego (Rate Limit):
r.GET("/swagger/*any", RateLimitMiddleware(), ginSwagger.WrapHandler(swaggerFiles.Handler))
- Nigdy nie ujawniaj wewnętrznych szczegółów:
- Nie dokumentuj wewnętrznych punktów końcowych
- Unikaj bezpośredniego ujawniania schematów bazy danych
- Dezinfektuj komunikaty o błędach w dokumentacji
Podsumowanie
Dodanie dokumentacji Swagger do Twojego API w Go zmienia doświadczenie deweloperskie z zgadywania na sterowaną eksplorację. Biblioteka swaggo sprawia, że ten proces jest prosty, generując kompleksową dokumentację OpenAPI z adnotacji w Twoim kodzie.
Najważniejsze wnioski:
- Zacznij od podstawowych adnotacji i rozwijaj je stopniowo
- Utrzymuj synchronizację dokumentacji z kodem za pomocą CI/CD
- Używaj Swagger UI do interaktywnego testowania podczas rozwoju
- Dokładnie dokumentuj uwierzytelnianie, błędy i przypadki brzegowe
- Rozważ implikacje bezpieczeństwa przy udostępnianiu dokumentacji
Niezależnie od tego, czy budujesz mikrousługi, publiczne API, czy narzędzia wewnętrzne, dokumentacja Swagger przynosi korzyści w postaci zmniejszenia obciążenia wsparcia, szybszego onboardingu i lepszego projektowania API. Początkowa inwestycja w naukę składni adnotacji szybko staje się rutyną, a automatyczne generowanie zapewnia, że Twoja dokumentacja nigdy nie pozostaje w tyle za implementacją.
Dla deweloperów Go połączenie mocnego typowania, generowania kodu i systemu adnotacji swaggo tworzy potężny przepływ pracy, który czyni dokumentację API naturalną częścią procesu developmentu, a nie czymś doklejanym na końcu.
Jeśli stos HTTP w dalszym ciągu pozostaje otwartym pytaniem, Frameworki Web w Go w 2026 roku: porównanie net/http, chi, Gin, Echo, Fiber benchmarkuje wszystkie pięć opcji na tym samym obciążeniu, więc decyzja o frameworku może zostać podjęta, zanim zostanie podłączony zestaw narzędzi do dokumentacji.
Przydatne linki
- Ściągawka Go
- Budowanie API REST w Go
- Frameworki Web w Go w 2026 roku: porównanie net/http, chi, Gin, Echo, Fiber
- Porównanie ORM w Go dla PostgreSQL: GORM vs Ent vs Bun vs sqlc
- Wzorce bazy danych dla wielu najemców (Multi-Tenancy) z przykładami w Go
- LLM ze strukturalizowanym wyjściem: Ollama, Qwen3 oraz Python lub Go
- Wydajność AWS Lambda: JavaScript vs Python vs Golang