Starlette 1.0: tras 8 años, el motor de FastAPI es estable

Actualizado el 05/08/2026 — Este artículo fue actualizado con pasos concretos de instalación, guía de migración detallada y respuestas a las preguntas más frecuentes sobre Starlette 1.0 en el ecosistema de FastAPI y agentes de IA.

Starlette 1.0.0 llegó el 22 de marzo de 2026, tras casi ocho años en versión 0.x, y marcó el final del ZeroVer en el framework ASGI más importante de Python. Hoy, en agosto, Starlette 1.3.1 es la versión estable en producción. El salto a semver estricto no fue cosmético: eliminó todas las APIs deprecadas, reestructuró el manejo de middleware y cambió cómo escribís tests. Para quienes trabajan con FastAPI, servidores MCP de Claude, o cualquier infraestructura async en Python, este cambio determina si tu código sigue funcionando en 2026 o necesita reescritura. Para más detalles, consultá nuestra novedades del compilador JIT en Python.

Starlette es un framework ASGI ligero y minimalista para Python que implementa el protocolo asincrónico ASGI (Asynchronous Server Gateway Interface), el estándar moderno para aplicaciones web async. No incluye ORM, validación de datos, ni templates — eso lo agrega FastAPI con Pydantic. Starlette te da HTTP async, routing, WebSockets, middleware y manejo de excepciones en forma async-native. Según el mantenedor Marcelo Trylesinski, el framework pasó de 57 millones a 325 millones de descargas mensuales en un año. Es infraestructura invisible que sostiene medio ecosistema Python: cada servidor MCP, cada instancia de FastAPI en producción, cada agente de IA que exponés en Python corre sobre Starlette por debajo.

Actualización (05/08/2026): Starlette 1.3.1 completó su primer ciclo de estabilidad con ciclos de patch sin breaking changes. El ecosistema lo considera production-ready de forma unánime. FastAPI 0.138+ y MCP SDK 0.7.0+ la requieren obligatoriamente.

  • Versión actual: Starlette 1.3.1 — dos meses en producción (desde junio 2026) sin breaking changes en el ciclo 1.x, validando que el cambio a semver fue acertado.
  • Adoption crítica: FastAPI 0.138+ requiere Starlette 1.0+ de forma obligatoria desde junio 2026. Todo proyecto que actualice FastAPI después de esa fecha tira Starlette 1.0 como dependencia forzada.
  • MCP SDK 0.7.0+: El Python Model Context Protocol SDK (que usan todos los servidores de agentes IA) requiere Starlette 1.0+. Cualquier servidor MCP en producción hoy corre sobre Starlette 1.x internamente.
  • Migraciones completadas: Los 325 millones de descargas mensuales provienen mayormente de FastAPI, que ya está alineado con 1.0. La migración del ecosistema fue más suave de lo esperado.

En 30 segundos

  • Starlette 1.0.0 salió el 22 de marzo de 2026 tras RC1 en febrero — fin de ZeroVer (versión 0.x), inicio de semver estricto
  • Versión actual: 1.3.1 desde julio 2026, completamente estable en producción con cero breaking changes desde 1.0
  • 325 millones de descargas mensuales (casi 10 millones por día), según Marcelo Trylesinski. Más instalaciones que FastAPI a solas.
  • Requiere Python 3.10 o superior — no podés actualizar si seguís en 3.9 o inferior
  • APIs eliminadas: on_startup, on_shutdown, @app.route() — reemplazos: lifespan, app.add_route(), Router explícito
  • BaseHTTPMiddleware cambió su comportamiento interno — afecta miles de proyectos con middleware personalizado
  • TestClient usa httpx en vez de requests — API similar pero no idéntica; afecta la suite de tests
  • Dependencia directa del Python MCP SDK — toda infraestructura de agentes IA en Python pasa por acá
  • FastAPI 0.138+ requiere Starlette 1.0+ obligatoriamente desde junio 2026
  • La migración es mecánica pero obligatoria: seguir pasos concretos evita 90% de los errores

¿Qué diferencia hay entre Starlette, FastAPI y Django?

Starlette es un framework ASGI minimalista creado en 2018 por Tom Christie. FastAPI es un constructor sobre Starlette que agrega validación automática con Pydantic y documentación OpenAPI. Django es un framework full-stack completamente distinto. Entender las diferencias es crítico para elegir qué actualizar.

  • Starlette: Framework async mínimo. Solo HTTP + WebSockets + middleware + manejo de excepciones. ASGI nativo. Cero opinión sobre ORM, templates o estructura de proyecto. Control total, overhead cero.
  • FastAPI: Constructor sobre Starlette. Agrega validación Pydantic, documentación OpenAPI automática, async-first, y convenciones de naming que te aceleran el desarrollo. Es Starlette + Pydantic + azúcar sintáctico.
  • Django: Framework full-stack WSGI (síncrono). Incluye ORM, templates, admin, autenticación, sesiones. Para sitios web tradicionales. Si necesitás velocidad de desarrollo y no te importa overhead, Django wins.

La mayoría de la gente que cree que usa “Starlette puro” en realidad está dentro de FastAPI. Starlette a solas es infraestructura que usan otros: MCP SDK, librerías internas, y proyectos que necesitan control total. FastAPI es lo que ves en tutoriales y startups que quieren API REST rápida.

¿Por qué Starlette es async y qué ventaja tiene frente a Django?

Async permite que un proceso Python maneje cientos de requests concurrentes en un solo thread sin necesidad de threads separados. Django en WSGI usa threads — un thread por request. Con Starlette/ASGI, un event loop async coordina todas las requests en un solo thread usando I/O no bloqueante.

Ejemplo práctico: tu API recibe 100 requests que hacen una query a una API externa que tarda 2 segundos. En Django (threads), necesitás 100 threads corriendo simultáneamente, cada uno bloqueado esperando respuesta. En Starlette (async), un solo thread maneja los 100 sin bloquearse: mientras uno espera I/O, otro procesa headers, otro envía respuesta. El event loop intercala todo automáticamente.

  • Consumo de memoria: Starlette usa ~10x menos memoria que Django para la misma cantidad de requests concurrentes. Un thread ocupa ~8MB. Async ocupa ~50KB.
  • Latencia: Una request no espera a que otra termine. En Django, si hay 50 threads esperando I/O, la request 51 espera a que se libere un slot. En Starlette, las 51 avanzan todas juntas.
  • Desventaja: complejidad mental: Async en Python requiere entender callbacks, await, event loops, y traps como no bloquear nunca. Django es más intuitivo si vienes de PHP.
  • Caso de uso Django aún válido: Si armás un blog CMS, admin, o app monolítica, Django sigue siendo best-of-breed. El overhead de async no importa si no tenés concurrencia.

¿Por qué Starlette pasó de ZeroVer a versión 1.0?

ZeroVer es una práctica en el ecosistema Python: nunca llegar a 1.0 para evitar la responsabilidad de versionado semántico. Starlette vivió 8 años en 0.x, lo que significaba que cualquier minor (0.45 → 0.46) podía romper APIs sin avisar. Eso cambió en marzo de 2026 por decisión consensuada de la comunidad.

La RC1 salió el 23 de febrero de 2026, y la versión final 1.0.0 llegó el 22 de marzo. Marcelo Trylesinski fue claro en el anuncio: esto no es un rewrite, es una declaración de estabilidad. A partir de ahora, si algo rompe, sube la major (1.1, 2.0). Si agrega funcionalidad sin romper, sube la minor (1.1). Si es solo un patch, sube el patch (1.0.1). Predecible.

El desarrollo contó con aportes de Adrian Garcia Badaracco (Pydantic), Alex Grönholm (async Python), Sebastián Ramírez (mantenedor de FastAPI), y otros. Fue decisión consensuada que Starlette, con 325 millones de descargas mensuales, merecía ese compromiso de estabilidad.

¿Qué APIs fueron eliminadas en Starlette 1.0?

Starlette 1.0 sacó todo lo que venía marcado como deprecado en 0.x. Algunas de estas APIs las usaba mucha gente. Si todavía estás en 0.x y querés actualizar, necesitás saber cuál es el reemplazo de cada una.

API eliminadaReemplazoImpactoEn qué archivo
on_startup / on_shutdownContexto lifespanAlto — muy usado en FastAPImain.py, app.py
@app.route() / @app.websocket_route()app.add_route() o Router explícitoMedio — común en tutorialesRutas principales
add_event_handler(“startup”)Contexto lifespanMedioInicialización
TestClient basado en requestsTestClient basado en httpxAlto en teststests/
Jinja2Templates sin jinja2 instaladopip install jinja2 explícitoBajorequirements.txt
JSONResponse sin contentParámetro content obligatorioBajoResponse handlers
Python < 3.10Actualizar a Python 3.10+Variable según proyectoruntime, Dockerfile

El cambio más disruptivo no está en esa tabla. Es BaseHTTPMiddleware, y merece su propia sección porque afecta a miles de proyectos en producción.

¿Cómo cambió BaseHTTPMiddleware en Starlette 1.0?

BaseHTTPMiddleware es cómo miles de desarrolladores escriben middleware personalizado en Starlette. Su comportamiento interno se reestructuró en 1.0 para mejorar eficiencia en async. Para la mayoría de los casos, el código sigue funcionando sin cambios. Pero hay casos donde necesita ajuste.

El problema en 0.x: Si extendías de BaseHTTPMiddleware, podías hacer prácticamente cualquier cosa en el método dispatch(). Bloqueabas la request, la modificabas, la rechazabas, o generabas respuestas custom. Funcionaba, pero internamente era ineficiente en async.

En Starlette 1.0: BaseHTTPMiddleware recibió una reestructuración interna para mejorar el flujo de datos y evitar contención en async. Externamente, la API se mantiene igual. Pero si tu middleware hace esto, puede necesitar ajuste:

  • Modifica headers de request o response: Puede necesitar ajuste si las modificaciones interactúan de forma inesperada con otro middleware o si depende del timing de la modificación.
  • Maneja excepciones en dispatch: El stack de manejo de errores cambió. Algunos try/except pueden no capturar lo que esperabas en 0.x.
  • Usa state de Starlette de forma no convencional: La propagación de state a través del middleware se hizo más estricta. Lo que funcionaba por side effects en 0.x puede fallar silenciosamente en 1.0.
  • Bloquea la request (valida, rechaza): Ahora requiere ser explícito con la respuesta. En 0.x podías dejar que el sistema lo infiera; en 1.0 no.

Si tenés middleware personalizado y falló al actualizar a 1.0, probablemente esté en uno de esos casos. La solución más robusta es migrar a ASGI Middleware puro — escribir el middleware directamente en el nivel ASGI en lugar de extender BaseHTTPMiddleware. Es un poco más verbose, pero te da control total y no depende de implementación interna de BaseHTTPMiddleware.

Ejemplo: middleware de autenticación en Starlette 1.0

En Starlette 0.x, un middleware de autenticación típico se veía así:

from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import JSONResponse

class AuthMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
token = request.headers.get("authorization")
if not token:
return JSONResponse({"error": "no token"}, status_code=401)
request.state.token = token
response = await call_next(request)
return response

En Starlette 1.0, eso probablemente siga funcionando. Pero para garantizar robustez en producción, mejor reescribilo como ASGI middleware puro:

from starlette.responses import JSONResponse

async def auth_middleware(app, scope, receive, send):
if scope["type"] != "http":
await app(scope, receive, send)
return

headers = dict(scope.get("headers", []))
token = headers.get(b"authorization", b"").decode()

if not token:
response = JSONResponse({"error": "no token"}, status_code=401)
await response(scope, receive, send)
return

scope["state"] = {"token": token}
await app(scope, receive, send)

Es más explícito, pero no depende de la abstracción BaseHTTPMiddleware. Así lo agregás a tu app:

from starlette.middleware import Middleware

app = Starlette(
middleware=[
Middleware(auth_middleware)
]
)

¿Qué se mejoró en Starlette entre 1.0 y 1.3.1?

Starlette 1.0 fue el milestone de estabilidad. Desde entonces hasta 1.3.1 (julio 2026), el proyecto publicó fixes y optimizaciones sin breaking changes. Los cambios no son features grandiosas, pero importan en producción.

  • WebSocket close reason (1.0+): Ahora podés rechazar una conexión WebSocket con un motivo HTTP (403, 401, etc.) antes de aceptarla. En 0.x tenías que dejar que la conexión se abriera y cerrarla adentro.
  • MultiPart parser eficiencia (1.0+): Si un cliente mandaba mil campos duplicados en un form, el parser acumulaba todos. Ahora respeta el límite de campos explícitamente.
  • Session change tracking (1.0+): Las sesiones saben si fueron modificadas en la request actual. Permite optimizaciones de cache — no regrabas sesiones que no cambiaron.
  • Fixes acumulados en 1.1-1.3: Correcciones de edge cases en routing, mejor manejo de excepciones en lifespan, y optimizaciones de memoria en el event loop.
  • Documentación mejorada: Las release notes de cada version fueron mucho más detalladas que en la era 0.x, facilitando migraciones.

¿Cómo impacta Starlette 1.0 a los proyectos FastAPI?

FastAPI hereda directamente de Starlette. Si actualizás Starlette a 1.0, tu app FastAPI se ve afectada. La buena noticia es que Sebastián Ramírez (Tiangolo), mantenedor de FastAPI, estuvo en contacto durante todo el desarrollo de 1.0. FastAPI internamente no usaba las APIs removidas.

Donde sí te puede pegar es en tu propio código FastAPI: si usás on_startup o on_shutdown directo, tenés que migrar a lifespan. Ejemplo de lo viejo:

@app.on_event("startup")
async def startup():
# abrir conexión a DB
db.connect()

Lo nuevo en Starlette 1.0+ y FastAPI compatible:

from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app):
# startup: abrir conexión a DB
db.connect()
yield
# shutdown: cerrar conexión
db.close()

app = FastAPI(lifespan=lifespan)

Antes de hacer pip install --upgrade fastapi (que arrastrar Starlette 1.0), corré tus tests. Si tenés middleware personalizado, revisá la sección anterior sobre BaseHTTPMiddleware. FastAPI 0.138+ es compatible; versiones anteriores pueden tener incompatibilidades menores.

¿Por qué el Python MCP SDK requiere Starlette 1.0?

El Python Model Context Protocol SDK — el kit de desarrollo para servidores de Claude y otros agentes IA — requiere Starlette 1.0+ desde la versión 0.7.0. Esto significa que si construís herramientas para Claude, agentes con OpenAI, o cualquier sistema que exponga capacidades vía MCP, tu servidor corre sobre Starlette 1.0. Para más detalles, consultá nuestra la adquisición de Astral por OpenAI.

Tener una versión 1.0 estable con semver en Starlette no es cosmético acá. Significa que podés depender del framework para producción sin preocuparte de que un bump de versión rompa la integración con tu servidor MCP. Para equipos que despliegan agentes de IA en infraestructura Python, esa previsibilidad vale mucho.

  • MCP SDK 0.7.0+ requiere Starlette 1.0: Validación de que la comunidad de IA confía en esta versión para producción.
  • Cada servidor MCP corre sobre Starlette internamente: No es opcional ni abstracto — es tu servidor HTTP/WebSocket.
  • Implicancia: Si desplegás servidores MCP en VPS, contenedores, o la cloud, estás en Starlette 1.x de facto.

Pasos concretos para instalar Starlette 1.0

No es solo hacer pip install starlette. Actualizar Starlette impacta todo lo que depende de él. Estos pasos evitan 90% de los problemas comunes.

  • Paso 1: verificá tu versión de Python. Abrí terminal y corré python --version. Necesitás 3.10 o superior. Si estás en 3.9, primero actualizá Python.
  • Paso 2: creá un entorno virtual limpio. Acá probás sin romper nada: python -m venv test_env + source test_env/bin/activate (Mac/Linux) o test_env\Scripts\activate (Windows).
  • Paso 3: buscá todos los on_startup y on_shutdown en tu código. Terminal: grep -r "on_startup\|on_shutdown" . Apuntá los archivos donde aparecen.
  • Paso 4: actualización por pasos. Si usás FastAPI: pip install --upgrade fastapi starlette. FastAPI 0.138+ trae Starlette 1.0 como dependencia. Si usás Starlette puro: pip install --upgrade starlette>=1.0.0.
  • Paso 5: corré tu suite de tests. Terminal: pytest (o tu test runner). Si falla, revisá los errores — la mayoría se debe a TestClient o lifespan.
  • Paso 6: si tenés Jinja2Templates, verificá dependencies. pip install jinja2 debe estar explícito en requirements.txt. En 0.x era opcional.
  • Paso 7: probá manualmente en local. Abrí http://localhost:8000 si es una API. Hace un par de requests. Buscá errores en los logs.
  • Paso 8: si todo ok, actualización de producción. Esperá a que pasés al menos un ciclo de testing (1-2 días). Deplegá en staging antes que en prod.

Guía paso a paso para migrar tu proyecto a Starlette 1.0

No alcanza con hacer pip install --upgrade. Tenés que revisar tu código manualmente. Acá va un checklist concreto de qué cambiar.

Paso 1: migrar on_startup y on_shutdown a lifespan

Si tu app FastAPI o Starlette pura usa @app.on_event(“startup”) o add_event_handler, eso está deprecado. Reemplazalo con un context manager lifespan. Ejemplos reales:

Lo viejo (0.x):

@app.on_event("startup")
async def startup():
global db
db = await Database("postgres://...").connect()
print("DB conectada")

@app.on_event("shutdown")
async def shutdown():
await db.disconnect()
print("DB desconectada")

Lo nuevo (1.0+):

from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app):
# startup code
global db
db = await Database("postgres://...").connect()
print("DB conectada")
yield
# shutdown code
await db.disconnect()
print("DB desconectada")

app = FastAPI(lifespan=lifespan)

El código después del yield corre al cerrar la app. Es más explícito y funciona exactamente igual.

Si usás decoradores @app.route(), migra a Router explícito o app.add_route(). Ejemplo:



Te puede interesar...