go-api-design
Acerca de
Esta habilidad de Claude proporciona patrones de diseño de API REST y gRPC para servicios en Go, abarcando manejadores HTTP, middleware, enrutamiento y documentación de API. Úsala al diseñar APIs, implementar middleware, estructurar endpoints REST o configurar servicios gRPC. Incluye orientación práctica sobre versionado, paginación, cierre elegante y documentación OpenAPI.
Instalación rápida
Claude Code
Recomendadonpx skills add eduardo-sl/go-agent-skills -a claude-code/plugin add https://github.com/eduardo-sl/go-agent-skillsgit clone https://github.com/eduardo-sl/go-agent-skills.git ~/.claude/skills/go-api-designCopia y pega este comando en Claude Code para instalar esta habilidad
Documentación
Go API Design
APIs are contracts. Once published, they're promises. Design them as if you'll maintain them for a decade — because you probably will.
1. HTTP Handler Structure
Use the standard http.Handler interface:
// ✅ Good — method on a struct with dependencies
type UserHandler struct {
store UserStore
logger *slog.Logger
}
func (h *UserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
h.handleGet(w, r)
case http.MethodPost:
h.handleCreate(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}
Handler function signature pattern:
// Handler methods return nothing — they write directly to ResponseWriter.
// Errors are handled inside the handler, not returned.
func (h *UserHandler) handleGet(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
id := chi.URLParam(r, "id") // or mux.Vars(r)["id"]
if id == "" {
h.respondError(w, http.StatusBadRequest, "missing user id")
return
}
user, err := h.store.GetByID(ctx, id)
if err != nil {
if errors.Is(err, ErrNotFound) {
h.respondError(w, http.StatusNotFound, "user not found")
return
}
h.logger.Error("get user", slog.Any("error", err))
h.respondError(w, http.StatusInternalServerError, "internal error")
return
}
h.respondJSON(w, http.StatusOK, user)
}
JSON response helpers:
func (h *UserHandler) respondJSON(w http.ResponseWriter, status int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
if err := json.NewEncoder(w).Encode(data); err != nil {
h.logger.Error("encode response", slog.Any("error", err))
}
}
func (h *UserHandler) respondError(w http.ResponseWriter, status int, msg string) {
h.respondJSON(w, status, map[string]string{"error": msg})
}
2. Middleware Pattern
Middleware wraps handlers. Use the standard func(http.Handler) http.Handler signature:
func RequestID(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id := r.Header.Get("X-Request-ID")
if id == "" {
id = uuid.New().String()
}
ctx := context.WithValue(r.Context(), requestIDKey, id)
w.Header().Set("X-Request-ID", id)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func Recoverer(logger *slog.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if rec := recover(); rec != nil {
logger.Error("panic recovered",
slog.Any("panic", rec),
slog.String("stack", string(debug.Stack())),
)
http.Error(w, "internal server error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
}
Middleware ordering (outside → inside):
Recoverer → RequestID → Logger → Auth → RateLimit → Handler
Recover MUST be outermost. Auth before business logic. Logger captures timing.
3. Request Validation
Decode and validate in one step:
type CreateUserRequest struct {
Name string `json:"name" validate:"required,min=2,max=100"`
Email string `json:"email" validate:"required,email"`
}
func decodeAndValidate[T any](r *http.Request) (T, error) {
var req T
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return req, fmt.Errorf("decode: %w", err)
}
if err := validate.Struct(req); err != nil {
return req, fmt.Errorf("validate: %w", err)
}
return req, nil
}
Limit request body size:
r.Body = http.MaxBytesReader(w, r.Body, 1<<20) // 1 MB
4. URL and Naming Conventions
GET /api/v1/users → list users
POST /api/v1/users → create user
GET /api/v1/users/{id} → get user
PUT /api/v1/users/{id} → replace user
PATCH /api/v1/users/{id} → partial update
DELETE /api/v1/users/{id} → delete user
GET /api/v1/users/{id}/orders → list user orders (nested resource)
Rules:
- Plural nouns for resources:
/users, not/user - Kebab-case for multi-word paths:
/order-items - camelCase for JSON fields:
"createdAt","firstName" - Version in URL path:
/api/v1/... - No verbs in URLs:
/users/search?q=alice, NOT/searchUsers
5. Pagination
type PageRequest struct {
Cursor string `json:"cursor"`
Limit int `json:"limit"`
}
type PageResponse[T any] struct {
Items []T `json:"items"`
NextCursor string `json:"next_cursor,omitempty"`
HasMore bool `json:"has_more"`
}
Prefer cursor-based pagination over offset/limit for large datasets. Offset pagination breaks under concurrent writes.
6. Graceful Shutdown
func main() {
srv := &http.Server{
Addr: ":8080",
Handler: router,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
}
// Start server
go func() {
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("server error: %v", err)
}
}()
// Wait for interrupt
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
// Graceful shutdown with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("shutdown error: %v", err)
}
log.Println("server stopped gracefully")
}
Programs should exit only in main(), preferably at most once.
7. Health Check Endpoints
// Liveness: is the process alive?
// GET /healthz → 200 OK
// Readiness: can the process serve traffic?
// GET /readyz → 200 OK or 503 Service Unavailable
func (h *HealthHandler) handleReady(w http.ResponseWriter, r *http.Request) {
if err := h.db.PingContext(r.Context()); err != nil {
h.respondError(w, http.StatusServiceUnavailable, "database unavailable")
return
}
h.respondJSON(w, http.StatusOK, map[string]string{"status": "ready"})
}
8. Error Response Format
Consistent error responses across the entire API:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "invalid request parameters",
"details": [
{"field": "email", "message": "must be a valid email"}
]
}
}
Map internal errors to HTTP status codes at the handler boundary. Internal errors should NEVER leak to clients.
Repositorio GitHub
Preguntas frecuentes
¿Qué es el Skill go-api-design?
go-api-design es un Skill de Claude creado por eduardo-sl. Los Skills agrupan instrucciones y recursos que Claude carga cuando los necesita para realizar tareas relacionadas con go-api-design sin indicaciones adicionales.
¿Cómo instalo go-api-design?
Usa los comandos de instalación de esta página: añade go-api-design a Claude Code como plugin o clona su repositorio en tu directorio de skills y reinicia Claude para cargarlo.
¿A qué categoría pertenece go-api-design?
go-api-design pertenece a la categoría Diseño.
¿Se puede usar go-api-design gratis?
Sí. go-api-design aparece en AIMCP y se puede instalar gratis.
Habilidades relacionadas
Utilice la habilidad executing-plans cuando tenga un plan de implementación completo para ejecutar en lotes controlados con puntos de revisión. Esta habilidad carga y revisa críticamente el plan, luego ejecuta tareas en pequeños lotes (por defecto 3 tareas) mientras reporta el progreso entre cada lote para la revisión del arquitecto. Esto asegura una implementación sistemática con puntos de control de calidad integrados.
Esta habilidad despacha un subagente revisor de código para analizar los cambios en el código frente a los requisitos antes de proceder. Debe usarse después de completar tareas, implementar funciones principales o antes de fusionar con la rama principal. La revisión ayuda a detectar problemas de forma temprana al comparar la implementación actual con el plan original.
Esta habilidad proporciona una guía integral para que los desarrolladores conecten servidores MCP a Claude Code mediante transportes HTTP, stdio o SSE. Cubre la instalación, configuración, autenticación y seguridad para integrar servicios externos como GitHub, Notion y APIs personalizadas. Úsala al configurar integraciones MCP, al configurar herramientas externas o al trabajar con el Protocolo de Contexto del Modelo de Claude.
Esta habilidad ayuda a los desarrolladores a elegir entre las interfaces web y CLI de Claude Code mediante el análisis de tareas, y luego permite la teletransportación fluida de sesiones entre estos entornos. Optimiza el flujo de trabajo gestionando el estado y el contexto de la sesión al cambiar entre web, CLI o móvil. Úsala para proyectos complejos que requieren diferentes herramientas en varias etapas.
