Files
fn_registry/functions/infra/spa_handler.md
T
egutierrez 7849e1cc8e feat(registry): random_hex_id_go_core + spa_handler_go_infra
Dos primitivas reutilizables para apps web del registry:
- random_hex_id_go_core: IDs hex aleatorios (apps con SQLite + IDs string)
- spa_handler_go_infra: http.Handler que sirve embed.FS con fallback
  a index.html (patron SPA para React Router/dnd-kit)

Ambas creadas via fn-constructor durante apps/kanban (issue 0053).
Tests pasan, fn index OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 15:54:01 +02:00

2.1 KiB

name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, params, output, tested, tests, test_file_path, file_path
name kind lang domain version purity signature description tags uses_functions uses_types returns returns_optional error_type imports params output tested tests test_file_path file_path
spa_handler function go infra 1.0.0 pure func SPAHandler(fsys fs.FS, indexFile string) http.Handler Retorna un http.Handler que sirve los archivos estaticos de un fs.FS y hace fallback a indexFile cuando el path no existe — patron SPA para React Router y similares.
http
spa
frontend
embed
fileserver
false
io/fs
net/http
strings
name desc
fsys fs.FS (tipico iofs.Sub de embed.FS) que contiene los archivos estaticos de la SPA
name desc
indexFile ruta dentro de fsys del HTML de fallback (ej. "index.html")
http.Handler que sirve archivos de fsys y hace fallback a indexFile cuando el path no existe — patron SPA para React Router true
sirve archivo estatico existente con status 200
ruta inexistente hace fallback a index.html con Content-Type text/html
raiz retorna index.html
path con .. retorna 400
functions/infra/spa_handler_test.go functions/infra/spa_handler.go

Ejemplo

//go:embed all:frontend/dist
var staticFiles embed.FS

func main() {
    sub, _ := iofs.Sub(staticFiles, "frontend/dist")
    spa := SPAHandler(sub, "index.html")

    mux := http.NewServeMux()
    mux.Handle("/api/", apiRouter())
    mux.Handle("/", spa)

    http.ListenAndServe(":8080", mux)
}

Notas

Funcion pura — construye el handler a partir de los parametros sin I/O. El handler resultante tiene los siguientes comportamientos:

  1. Si el path contiene .., responde 400 (defensa contra path traversal).
  2. Si el path normalizado corresponde a un archivo real en fsys (no directorio), lo sirve con http.FileServer.
  3. En cualquier otro caso (ruta no existe o es directorio), sirve indexFile con Content-Type: text/html; charset=utf-8 y status 200.
  4. Si indexFile no puede leerse, responde 500.

Pensado para apps que usan //go:embed all:frontend/dist y React Router (o cualquier router del lado del cliente). Centraliza el patron que de otro modo cada app escribiria inline.