Python Framework

FastAPI

FastAPI es el framework async de Python construido sobre type hints. Pydantic valida tus datos, Starlette gestiona el HTTP y la documentación de OpenAPI se escribe sola.

intermediate15 min readUpdated 16 sept 2026
main.py
python
# main.py
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: float


@app.post("/items", status_code=201)
async def create_item(item: Item) -> Item:
    return item
Lanzado
2018
Lenguaje
Python
Construido sobre
Starlette + Pydantic
Idea central
Type hints como contrato
Docs
OpenAPI y Swagger UI
Versión actual
0.11x

Por que importa

Por qué FastAPI parece magia (y no lo es)

Los tipos son la API

Declara un parámetro o un modelo con type hints y FastAPI valida las solicitudes, serializa las respuestas y documenta el endpoint a partir de la misma declaración.

Async cuando lo necesites

Construido sobre el toolkit ASGI de Starlette, por lo que los endpoints async gestionan I/O concurrente sin bloquear el event loop.

Documentación gratuita

Cada endpoint aparece en Swagger UI y ReDoc interactivos, generados a partir del esquema OpenAPI que FastAPI construye al iniciar.

La imagen completa

Tipos, Pydantic y dependencias

Los type hints describen el contrato, Pydantic lo hace cumplir y las dependencias inyectan las piezas compartidas que necesita una solicitud.

Type hints

Describir

Anotar los parámetros indica a FastAPI de dónde viene un valor y qué forma tiene, haciendo que la validación ocurra antes de que se ejecute tu función.

Modelos de Pydantic

Validar

Las clases BaseModel analizan, coercitan y rechazan datos, funcionando además como el contrato de respuesta y el esquema generado.

Dependencias

Inyectar

Depends() compone lógica compartida como sesiones, auth y configuraciones, resolviendo el grafo por solicitud y reutilizando resultados cacheados.

HTML5 de un vistazo

La caja de herramientas de FastAPI

Operaciones de ruta

@app.get y @app.post vinculan una URL y una función, con parámetros de ruta y query tipados.

Modelos de Pydantic

Las clases BaseModel validan los cuerpos de las solicitudes y dan forma a las respuestas.

Documentación automática

Swagger UI en /docs y ReDoc en /redoc, generados a partir de tu código.

Dependencias

Depends() inyecta sesiones, usuarios, paginación y configuración.

Soporte Async

Los endpoints async def se ejecutan en ASGI y esperan I/O de forma concurrente.

Tareas en segundo plano

Ejecuta trabajo después de enviar la respuesta sin necesidad de añadir una cola.

La guia completa

FastAPI: Todo lo que necesitas saber

¿Qué es FastAPI?

FastAPI es un framework web moderno de Python para construir APIs. Su idea fundamental es que tus type hints son el contrato. Declara un parámetro como int, un cuerpo como un modelo de Pydantic, o un tipo de retorno como ItemOut, y el framework valida la solicitud, serializa la respuesta y escribe la documentación; todo a partir de esa única declaración.

Está construido sobre dos librerías que deberías conocer por nombre. Starlette proporciona el toolkit ASGI: enrutamiento, middleware y el ciclo de solicitud/respuesta. Pydantic se encarga de la validación y la serialización. FastAPI es la capa que los combina, añade inyección de dependencias y genera un esquema de OpenAPI al iniciar.

El resultado es que una función pequeña y legible te proporciona un endpoint validado con documentación interactiva. La magia es real, pero es mecánica, y comprender esa mecánica es el objetivo de esta guía.

Operaciones de ruta

Una operación de ruta es una función vinculada a un método HTTP y una URL. El decorador nombra el método y la firma de la función le indica a FastAPI qué esperar.

# main.py
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello, world"}


@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
    return {"item_id": item_id, "q": q}

El segmento {item_id} es un parámetro de ruta, y su anotación int significa que FastAPI convierte la cadena y devuelve automáticamente un 422 si no puede hacerlo. Cualquier elemento que no esté en la ruta se trata como un parámetro de consulta, y un valor predeterminado de None lo hace opcional. No hay parseo manual ni request.args.

Agrupa operaciones relacionadas con un APIRouter, que puede llevar un prefijo y etiquetas para la documentación.

from fastapi import APIRouter

router = APIRouter(prefix="/items", tags=["items"])


@router.get("")
async def list_items():
    return []


@router.post("", status_code=201)
async def create_item():
    return {}

Luego incluye el router en la app: app.include_router(router). Así es como un proyecto de FastAPI se mantiene modular.

Modelos de Pydantic

Un modelo de Pydantic es una clase que hereda de BaseModel. Se encarga de parsear la entrada, forzar tipos compatibles, aplicar restricciones y lanzar un error de validación claro cuando algo falla.

# app/schemas.py
from datetime import datetime

from pydantic import BaseModel, Field


class ItemBase(BaseModel):
    name: str = Field(min_length=1, max_length=120)
    price: float = Field(gt=0)
    tags: list[str] = []


class ItemCreate(ItemBase):
    pass


class ItemOut(ItemBase):
    id: int
    created_at: datetime

    model_config = {"from_attributes": True}

Field añade restricciones y metadatos. model_config = {"from_attributes": True} permite que Pydantic lea atributos de un objeto ORM, para que puedas devolver una fila de la base de datos directamente y que sea serializada. La separación entre ItemCreate y ItemOut es deliberada: el modelo de entrada no tiene id, y el modelo de salida puede exponer campos que el cliente no puede definir.

Usa un modelo de Pydantic como el tipo del cuerpo (body) y FastAPI parseará, validará y te entregará un objeto tipado.

@app.post("/items", response_model=ItemOut, status_code=201)
async def create_item(item: ItemCreate):
    return await repo.create(item)

Si el cuerpo no es válido, FastAPI devuelve un 422 con una lista de los campos exactos que fallaron. No tuviste que escribir ni una sola línea de validación.

Cuerpos de solicitud y validación

FastAPI distingue las fuentes de datos mediante la posición y el tipo:

  • Los parámetros de ruta (path parameters) provienen de la URL.
  • Los parámetros de consulta (query parameters) son todo lo demás en la firma.
  • Un modelo de Pydantic es el cuerpo de la solicitud (request body).
  • Header, Cookie, Form y File marcan esas entradas especiales.
from fastapi import Header, Query


@app.get("/search")
async def search(
    q: str = Query(min_length=1, max_length=50),
    limit: int = Query(default=20, ge=1, le=100),
    user_agent: str | None = Header(default=None),
):
    return {"q": q, "limit": limit, "user_agent": user_agent}

Las restricciones en los query parameters se aplican de la misma manera que en los modelos, y la documentación generada muestra los límites. Esta consistencia es la razón por la cual el código de FastAPI se lee de forma tan compacta.

Documentación automática

Dado que el esquema se genera a partir del código, la documentación nunca queda obsoleta. Inicia la aplicación y abre /docs para acceder a Swagger UI, donde puedes llamar a los endpoints directamente, o /redoc para obtener una referencia más limpia.

app = FastAPI(
    title="Inventory API",
    version="1.0.0",
    description="Items, stock levels and orders.",
)

Puedes añadir resúmenes, descripciones y ejemplos a las operaciones de ruta y a los campos, y estos aparecerán en la documentación. Para los equipos de frontend, el mismo esquema puede generar clientes tipados, asegurando que la API y sus consumidores permanezcan sincronizados. Esta es la recompensa de tratar los tipos como el contrato.

Dependencias

Una dependencia es una función que FastAPI llama antes que tu endpoint y cuyo valor de retorno es inyectado. Depends() es el mecanismo, y es composible.

from fastapi import Depends, HTTPException


async def get_session() -> AsyncSession:
    async with SessionLocal() as session:
        yield session


async def get_current_user(
    token: str = Depends(get_token),
    session: AsyncSession = Depends(get_session),
) -> User:
    user = await authenticate(session, token)
    if user is None:
        raise HTTPException(status_code=401, detail="Not authenticated")
    return user
@app.get("/me", response_model=UserOut)
async def read_me(user: User = Depends(get_current_user)):
    return user

Las dependencias pueden ser síncronas o asíncronas, pueden usar yield para ejecutar una limpieza después de la respuesta, y se almacenan en caché dentro de una solicitud, por lo que get_session se llama una sola vez incluso si varias dependencias lo necesitan. Son el lugar idiomático para gestionar sesiones de base de datos, autenticación, paginación y feature flags.

Async frente a sync

FastAPI soporta endpoints tanto def como async def, y la elección es importante.

  • Un endpoint async def se ejecuta en el event loop. Cada llamada de I/O en su interior debe ser esperada (awaited) mediante una librería async, o de lo contrario bloqueará el loop para todas las demás solicitudes.
  • Un endpoint def convencional se ejecuta en un thread pool, por lo que las llamadas bloqueantes son seguras, pero cada solicitud ocupa un hilo.
# Async: use an async driver and await it
@app.get("/items")
async def list_items(session: AsyncSession = Depends(get_session)):
    result = await session.execute(select(Item))
    return result.scalars().all()


# Sync: blocking code is fine; FastAPI offloads it
@app.get("/report")
def build_report():
    return generate_report()  # CPU or blocking I/O

La regla es sencilla: async en todo el camino, o sync en todo el camino. Mezclar una llamada bloqueante dentro de un endpoint async def es el error de rendimiento más común en FastAPI, y es invisible hasta que llega el tráfico.

Errores y códigos de estado

Lanza HTTPException para devolver un error con un código de estado y un detalle en JSON. FastAPI lo convierte en una respuesta y documenta el código de estado.

from fastapi import HTTPException, status


@app.get("/items/{item_id}", response_model=ItemOut)
async def read_item(item_id: int):
    item = await repo.get(item_id)
    if item is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Item not found",
        )
    return item

Para mantener una estructura de error consistente en toda la API, registra manejadores de excepciones que conviertan los errores del framework y del dominio en un único envoltorio. Mantén el campo detail estable; los clientes lo utilizarán para el parseo.

Tareas en segundo plano

BackgroundTasks ejecuta trabajo después de que se envía la respuesta, dentro del mismo proceso. Es la opción ligera para enviar un correo electrónico o escribir una fila de auditoría.

from fastapi import BackgroundTasks


def send_receipt(email: str) -> None:
    ...


@app.post("/orders", status_code=201)
async def create_order(
    order: OrderCreate,
    tasks: BackgroundTasks,
):
    saved = await repo.create(order)
    tasks.add_task(send_receipt, saved.email)
    return saved

Las tareas en segundo plano no son una cola de trabajos. Se ejecutan en el proceso, por lo que se pierden si el worker se reinicia y compiten con el manejo de solicitudes. Para cualquier cosa que requiera durabilidad, delega la tarea a Celery, RQ o a una cola gestionada.

Bases de datos

FastAPI no tiene una capa de base de datos, por lo que debes elegir la tuya. Las opciones más comunes son SQLAlchemy con un motor async o SQLModel, que combina SQLAlchemy con Pydantic. El patrón de dependencias se encarga de suministrar la sesión y cerrarla después de la solicitud.

# app/db.py
from collections.abc import AsyncIterator

from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

engine = create_async_engine(settings.database_url, pool_pre_ping=True)
SessionLocal = async_sessionmaker(engine, expire_on_commit=False)


async def get_session() -> AsyncIterator[AsyncSession]:
    async with SessionLocal() as session:
        yield session
@router.post("", response_model=ItemOut, status_code=201)
async def create_item(
    payload: ItemCreate,
    session: AsyncSession = Depends(get_session),
):
    item = Item(**payload.model_dump())
    session.add(item)
    await session.commit()
    await session.refresh(item)
    return item

Usa expire_on_commit=False para que los objetos sigan siendo utilizables después del commit, y mantén las transacciones dentro de la solicitud. Si prefieres un driver síncrono, utiliza endpoints def estándar para que FastAPI pueda delegarlos al thread pool.

Seguridad

FastAPI incluye los componentes básicos para OAuth2 y JWT sin imponer un sistema de autenticación completo. OAuth2PasswordBearer extrae el token, y python-jose o pyjwt lo verifican.

from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/users/me", response_model=UserOut)
async def read_me(
    token: str = Depends(oauth2_scheme),
    session: AsyncSession = Depends(get_session),
):
    user = await get_user_from_token(session, token)
    if user is None:
        raise HTTPException(status_code=401, detail="Invalid token")
    return user

Almacena los hashes de las contraseñas con passlib o argon2-cffi, nunca en texto plano. Realiza el hash y la verificación fuera del endpoint, mantén los secretos en la configuración y añade rate limiting y CORS de manera deliberada. FastAPI te proporciona las herramientas; la política sigue siendo responsabilidad tuya.

Configuración y ajustes

Mantén la configuración en un único objeto tipado en lugar de leer variables de entorno en todo el código. pydantic-settings valida el entorno al iniciar la aplicación y te ofrece la misma seguridad de tipos que tus modelos.

# app/config.py
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_prefix="APP_")

    database_url: str
    secret_key: str
    access_token_minutes: int = 30


settings = Settings()
from functools import lru_cache

from app.config import Settings


@lru_cache
def get_settings() -> Settings:
    return Settings()
@app.get("/info")
async def info(settings: Settings = Depends(get_settings)):
    return {"token_minutes": settings.access_token_minutes}

Dado que los ajustes se inyectan como cualquier otra dependencia, las pruebas pueden sobrescribirlos con app.dependency_overrides[get_settings]. Si faltan variables obligatorias, la aplicación fallará al iniciar en lugar de hacerlo en la primera solicitud, que es precisamente cuando quieres detectarlo.

Pruebas

TestClient envuelve la aplicación en un cliente similar a WSGI y realiza solicitudes reales en el mismo proceso, incluyendo la validación y la anulación de dependencias (dependency overrides).

# tests/test_items.py
from fastapi.testclient import TestClient

from app.main import app

client = TestClient(app)


def test_create_item():
    response = client.post("/items", json={"name": "Cup", "price": 9.5})
    assert response.status_code == 201
    assert response.json()["name"] == "Cup"


def test_invalid_price():
    response = client.post("/items", json={"name": "Cup", "price": -1})
    assert response.status_code == 422

Usa app.dependency_overrides para sustituir la sesión de la base de datos por una de prueba, y escribe pruebas asíncronas con httpx.AsyncClient cuando necesites ejecutar endpoints asíncronos directamente. Debido a que las dependencias son explícitas, reemplazarlas en las pruebas es muy sencillo.

Mejores prácticas

  • Declara los modelos de request y response explícitamente; nunca aceptes ni devuelvas diccionarios simples.
  • Mantén los endpoints de async def totalmente async, o utiliza def para que FastAPI delegue el trabajo bloqueante.
  • Coloca la lógica compartida en dependencias de Depends() en lugar de repetirla en cada ruta.
  • Divide la aplicación con APIRouter y asigna a cada router un prefijo y etiquetas.
  • Añade restricciones con Field y Query en lugar de validar manualmente.
  • Lanza HTTPException con el código de estado correcto y un detail estable.
  • Sobrescribe las dependencias en los tests en lugar de hacer patching de los componentes internos.
  • Guarda los secretos en objetos de configuración leídos desde el entorno.

Errores comunes

  • Ejecutar código bloqueante dentro de un endpoint async def y congelar el event loop.
  • Retornar objetos ORM sin un response_model, filtrando columnas internas.
  • Olvidar from_attributes = True y preguntarse por qué falla la serialización.
  • Escribir un único main.py gigante en lugar de utilizar routers y módulos.
  • Tratar BackgroundTasks como una cola de trabajos persistente.
  • Compartir una única sesión entre peticiones o entre tareas asíncronas.
  • Validar la misma entrada dos veces porque se ignoró el modelo de Pydantic.
  • Capturar todas las excepciones y retornar un 200 con un cuerpo de error.

Próximos pasos

FastAPI convierte los type hints en una API validada y documentada, razón por la cual se ha convertido en la opción predeterminada para los nuevos servicios en Python. Para comparar las ventajas y desventajas, lee la guía de Flask para conocer el enfoque síncrono minimalista y la de Django para un framework completo con ORM y panel de administración. Después, profundiza en el contrato de la API con OpenAPI y las reglas de diseño en REST APIs, y asegúrate de que tus endpoints valgan la pena documentar definiendo correctamente el modelo de recursos.

En la practica

Un endpoint, cuatro capas

El modelo declara la forma, la operación de ruta lo expone, las dependencias suministran el contexto y la llamada a la base de datos hace el trabajo.

app/main.py
from fastapi import FastAPI, HTTPException

from app.schemas import ItemOut

app = FastAPI(title="Inventory API")


@app.get("/items/{item_id}", response_model=ItemOut)
async def read_item(item_id: int):
    item = await db.items.get(item_id)
    if item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    return item

Async e I/O bloqueante

Un endpoint async se ejecuta en el event loop, por lo que una llamada bloqueante detiene todas las demás solicitudes. Usa un driver async con async def, o un endpoint def simple que FastAPI ejecute en un thread pool.

Preferir
@app.get("/items")
async def list_items(session: AsyncSession = Depends(get_session)):
    result = await session.execute(select(Item))
    return result.scalars().all()
Evitar
@app.get("/items")
async def list_items():
    # blocks the event loop for the whole query
    return session.query(Item).all()

Dar forma a la respuesta

Un response_model filtra la salida a los campos declarados y lo documenta, evitando que columnas internas se filtren a los clientes.

Preferir
@app.get("/users/{user_id}", response_model=UserOut)
async def get_user(user_id: int):
    return await repo.get(user_id)
Evitar
@app.get("/users/{user_id}")
async def get_user(user_id: int):
    # returns password_hash and every other column
    return await repo.get(user_id)

Compromisos

¿Es FastAPI la opción predeterminada correcta?

FastAPI optimiza las APIs tipadas y el rendimiento async. Esto es excelente para servicios, pero incómodo cuando la app consiste principalmente en páginas renderizadas en el servidor.

Strengths

  • Menos código, menos bugs

    La validación, serialización y documentación provienen de los mismos type hints, por lo que no hay un segundo esquema que mantener sincronizado.

  • Documentación automática

    Un esquema OpenAPI vivo y docs interactivos hacen que la API sea explorable para los equipos de frontend y clientes desde el primer día.

  • Construido para el Python moderno

    Endpoints async, inyección de dependencias y Pydantic v2 lo hacen rápido y agradable sin necesidad de un framework pesado.

Trade-offs

  • Async tiene sus riesgos

    Una sola llamada bloqueante dentro de un endpoint async puede detener todo el worker. Debes saber qué librerías son realmente async.

  • Es un framework de API

    FastAPI no incluye plantillas, un panel de administración ni un ORM. Las apps renderizadas en servidor necesitan otra capa o un framework diferente.

  • El ecosistema es más joven

    Las versiones de Pydantic y SQLAlchemy avanzan rápido y los patrones cambian. Espera tener que mantener las dependencias actualizadas.

Preguntas frecuentes

Preguntas frecuentes

Keep learning

Related topics from the roadmap.

$ comienza a aprender

Listo para aprender FastAPI?

Nuestro tutorial interactivo te guia a traves de FastAPI paso a paso — con quizzes y codigo real que puedes ejecutar en el navegador.