Files
fn_registry/dev/issues/completed/0009-http-server.md
T

7.3 KiB

id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
id title status type domain scope priority depends blocks related created updated tags
0009 HTTP Server Foundation completado feature
multi-app alta
2026-05-17 2026-05-17

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_server y sqlite_api construyen su HTTP serving ad-hoc cada vez: router manual con http.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/http es 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 con http.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, HTTPError en functions/infra/ con .md en types/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 — serializa data a 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 — crea http.ServeMux y registra rutas con sus handlers
  • 3.6 http_servehttp.Server con 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 index y 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.ServeMux nativo: Go 1.22+ soporta path params y métodos en ServeMux, 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 error interface — 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.