BookinglyTech News
Software

FastAPI corrige un bug de orden de rutas en app.frontend() para SPA

La nueva función app.frontend() de FastAPI, disponible desde la versión 0.138.0, soluciona un error de prioridad de rutas que provocaba respuestas inesperadas al servir aplicaciones SPA.

2 min de lecturaDev.to0 vistas

FastAPI ha añadido app.frontend() como método unificado para servir aplicaciones de una sola página (SPA) y, al mismo tiempo, ha corregido un bug de orden de rutas que afectaba a los catch‑all declarados antes de las rutas de la API.

El problema aparecía cuando un desarrollador colocaba accidentalmente un catch‑all (@app.get("/{full_path:path}")) por encima de una ruta API como /api/ping. En ese caso, una petición a /api/ping devolvía el index.html de la SPA con código HTTP 200, sin ningún registro de error. El bug se reprodujo en versiones de FastAPI entre la 0.138.0 (20 junio) y la 0.141.0 (29 julio) de 2026.

Con la nueva API, app.frontend() reemplaza dos enfoques tradicionales:

  1. Montar StaticFiles en /, que servía los assets correctamente pero no manejaba rutas cliente‑side, provocando 404 en refrescos profundos.
  2. Definir un catch‑all manual que requería estar declarado después de todas las rutas API.

El autor del informe instaló FastAPI 0.141.1 y creó tres mini‑aplicaciones que servían el mismo directorio dist/ (contiene index.html y assets/app.js) junto a una ruta GET /api/ping. Variando el orden de declaración se observaron los siguientes comportamientos:

  • Catch‑all primero: curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8002/api/ping devolvió 200 y el HTML de la SPA.
  • StaticFiles primero: la misma petición retornó 404 y el JSON {"detail":"Not Found"}.
  • app.frontend() antes de include_router(): la petición siguió respondiendo 200 con {"pong":true}.

La clave está en que FastAPI almacena las rutas del frontend en _low_priority_routes y sólo las evalúa después de que todas las rutas normales fallen, independiente del orden del código. Así, el bug de prioridad desaparece.

Otro detalle importante es la gestión del encabezado Accept. El catch‑all tradicional devolvía index.html tanto para rutas cliente‑side como para assets inexistentes, lo que podía enmascarar errores de compilación. app.frontend() inspecciona el encabezado y, si contiene application/json, devuelve 404 en lugar de la SPA. Esto permite distinguir entre una navegación del cliente (text/html) y una petición de recurso que debería ser JSON.

Se detectaron también dos huecos menores:

  • Las peticiones HEAD a rutas catch‑all fallaban con 405 porque solo estaban registradas como GET. app.frontend() registra explícitamente {"GET", "HEAD"} y corrige este comportamiento.
  • Proteger la SPA con autenticación era complicado con StaticFiles, que no acepta el argumento dependencies. Con app.frontend() es posible aplicar dependencias a través de un APIRouter, encapsulando la protección de todo el frontend y sus assets.

En resumen, la incorporación de app.frontend() simplifica la configuración de SPAs en FastAPI, elimina la dependencia del orden de declaración de rutas y añade controles de encabezado y método que mejoran la robustez del servicio.