c226767ebd
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7.1 KiB
7.1 KiB
0009 — HTTP Server Foundation
Metadata
| Campo | Valor |
|---|---|
| ID | 0009 |
| Estado | pendiente |
| Prioridad | alta |
| Tipo | feature |
Dependencias
Ninguna.
Objetivo
Crear funciones reutilizables de HTTP server en Go (dominio infra) que permitan montar una API REST completa componiendo primitivas del registry, en vez de construir el serving desde cero en cada app.
Contexto
deploy_serverysqlite_apiconstruyen su HTTP serving ad-hoc cada vez: router manual conhttp.ServeMux, middleware inline, helpers de respuesta repetidos.- Existen funciones HTTP client (
http_get_json_go_infra,http_post_json_go_infra) pero CERO funciones de HTTP server. - Go stdlib
net/httpes suficiente como base — no se necesita framework externo, solo primitivas componibles encima de stdlib. - Con estas funciones, una app nueva que necesite API solo hace: registrar rutas + componer middlewares +
http_serve.
Arquitectura
functions/infra/
├── http_router.go — NEW: registro de rutas con path params y métodos
├── http_router.md — NEW
├── http_middleware_chain.go — NEW: composición de middlewares
├── http_middleware_chain.md — NEW
├── http_cors_middleware.go — NEW: middleware CORS configurable
├── http_cors_middleware.md — NEW
├── http_logger_middleware.go — NEW: middleware de logging request/response
├── http_logger_middleware.md — NEW
├── http_json_response.go — NEW: helper para escribir JSON responses
├── http_json_response.md — NEW
├── http_error_response.go — NEW: helper para escribir error responses estandar
├── http_error_response.md — NEW
├── http_parse_body.go — NEW: decode JSON body con validación de tamaño
├── http_parse_body.md — NEW
├── http_serve.go — NEW: ListenAndServe con graceful shutdown
├── http_serve.md — NEW
types/infra/
├── http_route.md — NEW: metadata del tipo Route
├── http_middleware.md — NEW: metadata del tipo Middleware
├── http_error.md — NEW: metadata del tipo HTTPError
Patrón pure core / impure shell
- Pure:
http_middleware_chain(composición de funciones),http_cors_middleware(retorna función sin I/O) - Impure:
http_router,http_json_response,http_error_response,http_parse_body,http_logger_middleware,http_serve— todos interactúan conhttp.ResponseWriter/http.Request/ red
Diseño
Tipos
// Middleware es un wrapper de http.Handler
type Middleware func(http.Handler) http.Handler
// Route define una ruta con método y handler
type Route struct {
Method string
Path string
Handler http.HandlerFunc
}
// HTTPError es un error estructurado para respuestas API
type HTTPError struct {
Status int `json:"status"`
Code string `json:"code"`
Message string `json:"message"`
}
Funciones
| Función | Purity | Firma (simplificada) |
|---|---|---|
http_router |
impure | (routes []Route) *http.ServeMux |
http_middleware_chain |
pure | (middlewares ...Middleware) Middleware |
http_cors_middleware |
pure | (origins []string, methods []string) Middleware |
http_logger_middleware |
impure | (logger io.Writer) Middleware |
http_json_response |
impure | (w http.ResponseWriter, status int, data any) |
http_error_response |
impure | (w http.ResponseWriter, err HTTPError) |
http_parse_body |
impure | (r *http.Request, dst any, maxBytes int64) error |
http_serve |
impure | (addr string, handler http.Handler, ctx context.Context) error |
Tareas
Fase 1: Tipos
- 1.1 Crear tipos
Route,Middleware,HTTPErrorenfunctions/infra/con.mdentypes/infra/
Fase 2: Funciones puras
- 2.1
http_middleware_chain— compone N middlewares en uno solo (reduce de derecha a izquierda) - 2.2
http_cors_middleware— retorna Middleware que setea headers CORS según config
Fase 3: Funciones impuras
- 3.1
http_json_response— serializadataa JSON, setea Content-Type, escribe status - 3.2
http_error_response— escribe HTTPError como JSON response - 3.3
http_parse_body— lee body con limit de tamaño, decode JSON, cierra body - 3.4
http_logger_middleware— loguea método, path, status, duración de cada request - 3.5
http_router— creahttp.ServeMuxy registra rutas con sus handlers - 3.6
http_serve—http.Servercon graceful shutdown vía context cancelable + señales OS
Fase 4: Tests y cleanup
- 4.1 Tests para cada función con
httptest.NewRecorder - 4.2
fn indexy verificar que todas las funciones aparecen en registry.db - 4.3 Verificar
go vet -tags fts5
Ejemplo de uso
// En cualquier app nueva:
routes := []infra.Route{
{Method: "GET", Path: "/health", Handler: healthHandler},
{Method: "GET", Path: "/api/items", Handler: listItems},
{Method: "POST", Path: "/api/items", Handler: createItem},
}
mux := infra.HttpRouter(routes)
middleware := infra.HttpMiddlewareChain(
infra.HttpCorsMiddleware([]string{"*"}, []string{"GET", "POST"}),
infra.HttpLoggerMiddleware(os.Stdout),
)
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
infra.HttpServe(":8080", middleware(mux), ctx)
// Dentro de un handler:
func listItems(w http.ResponseWriter, r *http.Request) {
items, err := db.GetItems()
if err != nil {
infra.HttpErrorResponse(w, infra.HTTPError{Status: 500, Code: "db_error", Message: err.Error()})
return
}
infra.HttpJsonResponse(w, 200, items)
}
func createItem(w http.ResponseWriter, r *http.Request) {
var input CreateItemInput
if err := infra.HttpParseBody(r, &input, 1<<20); err != nil {
infra.HttpErrorResponse(w, infra.HTTPError{Status: 400, Code: "bad_request", Message: err.Error()})
return
}
// ...
}
Decisiones de diseño
- Solo stdlib
net/http: sin chi, gin, echo. Las funciones wrappean stdlib para mantener zero-dependency. http.ServeMuxnativo: Go 1.22+ soporta path params y métodos enServeMux, suficiente sin router externo.- Middleware como
func(http.Handler) http.Handler: patrón estándar de Go, compatible con cualquier middleware de terceros. - Graceful shutdown con context: permite que la app controle cuándo parar (señales OS, context padre, etc.).
- HTTPError como struct simple: no implementa
errorinterface — es un DTO de respuesta, no un error Go.
Prerequisitos
Ninguno. Solo Go stdlib.
Riesgos
- Scope creep hacia un framework: Mitigado manteniendo cada función atómica y sin estado compartido. No es un framework, son funciones sueltas.
- Colisión con patterns existentes en apps: Las apps existentes pueden migrar gradualmente, no hay breaking change.