Luis MartinezLuis Martinez·

Litestar: APIs de Alto Rendimiento con Python

Construye APIs production-ready utilizando uno de los frameworks más modernos y performantes del ecosistema Python


Requisitos del proyecto

Para seguir este tutorial necesitas:

  • Python 3.12+ (recomendamos 3.13 para mejor rendimiento)
  • uv como gestor de dependencias (el más rápido del ecosistema Python)
  • Conocimientos básicos de Python y APIs REST

Nota: Si quieres aprender más sobre uv y sus ventajas, tenemos un post específico sobre gestión de dependencias con Python.


Litestar es el framework ASGI que está revolucionando la forma de construir APIs en Python. Con un diseño async-first y una arquitectura limpia, ofrece rendimiento extremo sin sacrificar la elegancia del código. En este tutorial exploraremos cómo construir una API production-ready usando Litestar 2.20+.

Por qué Litestar en 2026

Litestar se ha consolidado como una de las mejores opciones para construir APIs de alto rendimiento:

  • Rendimiento superior: Especialmente con msgspec como serializador, puede manejar 10k+ req/s
  • "Batteries included": DI, middlewares, WebSockets, OpenAPI, caching, todo integrado
  • Arquitectura limpia: Controllers clase-basados que escalan magnificamente
  • Full async: Diseñado desde cero para async/await
  • Type safety: Integración nativa con type hints y mypy

1. Inicialización del proyecto

1.1 Crear proyecto

# Crear el proyecto
uv init litestar-api
cd litestar-api

# Fijar versión de Python
uv python pin 3.13

Esto crea la estructura básica con pyproject.toml y uv.lock para dependencias reproducibles.

1.2 Añadir dependencias

# Core dependencies
uv add litestar uvicorn[standard]

# Serialización de alto rendimiento
uv add msgspec

# Base de datos (opcional)
uv add sqlalchemy asyncpg

# Desarrollo
uv add --dev pytest pytest-asyncio ruff mypy

Tu pyproject.toml:

[project]
name = "litestar-api"
version = "0.1.0"
description = "API de alto rendimiento con Litestar"
requires-python = ">=3.12"
dependencies = [
    "litestar>=2.20.0",
    "uvicorn[standard]>=0.34.0",
    "msgspec>=0.19.0",
    "sqlalchemy>=2.0.0",
    "asyncpg>=0.30.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0.0",
    "pytest-asyncio>=0.25.0",
    "ruff>=0.9.0",
    "mypy>=1.14.0",
]

2. Aplicación mínima async-first

app/main.py:

from litestar import Litestar, get

@get("/")
async def health() -> dict[str, str]:
    return {"status": "ok", "version": "1.0.0"}

@get("/health")
async def health_check() -> dict[str, str]:
    return {"status": "healthy", "service": "litestar-api"}

app = Litestar(
    route_handlers=[health, health_check],
    debug=True,
)

Ejecutar:

# Con uvicorn
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

# O con el CLI de Litestar
uv run litestar run --reload

Endpoints generados automáticamente:

  • /schema - OpenAPI schema
  • /docs - Documentación interactiva (Swagger UI, ReDoc, Stoplight)
  • /openapi.json - Especificación OpenAPI

3. Arquitectura recomendada

Estructura de proyecto production-ready:

litestar-api/
│
├── app/
│   ├── __init__.py
│   ├── main.py              # Entry point
│   ├── config.py            # Configuración (pydantic-settings)
│   ├── dependencies.py      # Inyección de dependencias
│   ├── controllers/         # Controladores por dominio
│   │   ├── __init__.py
│   │   └── users.py
│   ├── models/             # Modelos de datos
│   │   ├── __init__.py
│   │   └── user.py
│   ├── services/           # Lógica de negocio
│   │   ├── __init__.py
│   │   └── user_service.py
│   └── middleware/         # Middleware personalizado
│       └── __init__.py
│
├── tests/
│   ├── __init__.py
│   └── test_users.py
│
├── pyproject.toml
├── uv.lock
└── README.md

4. Controladores clase-basados

Los controllers permiten organizar endpoints por dominio de forma limpia:

from litestar import Controller, get, post, patch, delete
from litestar.dto import DTOConfig
from litestar.contrib.msgspec import MsgspecDTO
from msgspec import Struct
from uuid import UUID

# Modelo con msgspec (alto rendimiento)
class User(Struct):
    id: UUID
    name: str
    email: str
    age: int

class UserCreate(Struct):
    name: str
    email: str
    age: int

class UserUpdate(Struct):
    name: str | None = None
    email: str | None = None
    age: int | None = None

# DTO para respuestas parciales
class PartialUserDTO(MsgspecDTO[User]):
    config = DTOConfig(exclude={"id"}, partial=True)


class UserController(Controller):
    path = "/users"
    tags = ["Users"]

    # Inyección de servicio
    dependencies = {"user_service": Provide(get_user_service)}

    @get()
    async def list_users(
        self,
        user_service: UserService,
        limit: int = 100,
        offset: int = 0
    ) -> list[User]:
        return await user_service.list_users(limit, offset)

    @post()
    async def create_user(
        self,
        data: UserCreate,
        user_service: UserService
    ) -> User:
        return await user_service.create_user(data)

    @get(path="/{user_id:uuid}")
    async def get_user(
        self,
        user_id: UUID,
        user_service: UserService
    ) -> User:
        return await user_service.get_user(user_id)

    @patch(path="/{user_id:uuid}", dto=PartialUserDTO)
    async def update_user(
        self,
        user_id: UUID,
        data: UserUpdate,
        user_service: UserService
    ) -> User:
        return await user_service.update_user(user_id, data)

    @delete(path="/{user_id:uuid}")
    async def delete_user(
        self,
        user_id: UUID,
        user_service: UserService
    ) -> None:
        await user_service.delete_user(user_id)

Registro en la aplicación:

from litestar import Litestar
from app.controllers.users import UserController

app = Litestar(
    route_handlers=[UserController],
    debug=True,
)

5. Serialización de alto rendimiento con msgspec

Litestar soporta múltiples backends de serialización:

BackendRendimientoCaso de uso
msgspec⭐⭐⭐⭐⭐APIs de alto throughput
Pydantic v2⭐⭐⭐⭐Validación compleja
dataclasses⭐⭐⭐APIs simples
attrs⭐⭐⭐Casos específicos

Ejemplo completo con msgspec

import msgspec
from litestar import post
from litestar.contrib.msgspec import MsgspecPlugin

# Structs son inmutables y typed
class Product(msgspec.Struct):
    id: int
    name: str
    price: float
    tags: list[str] = []

class Order(msgspec.Struct):
    id: int
    products: list[Product]
    total: float

@post("/orders")
async def create_order(data: Order) -> Order:
    # msgspec valida y deserializa automáticamente
    # 2-5x más rápido que Pydantic v2
    return data

# Configurar msgspec como serializador global
from litestar.serialization import DEFAULT_SERIALIZATION_BACKEND
from litestar.contrib.msgspec import MsgspecPlugin

app = Litestar(
    route_handlers=[create_order],
    plugins=[MsgspecPlugin()],
)

Beneficios técnicos de msgspec:

  • Parsing binario optimizado en C
  • Zero-copy deserialization donde sea posible
  • Menor overhead de memoria que Pydantic
  • Soporte nativo para JSON, MessagePack, YAML, TOML

6. Inyección de Dependencias (DI)

Sistema explícito y poderoso basado en Provide:

from litestar.di import Provide
from litestar import Litestar
from contextlib import asynccontextmanager

# Servicio de base de datos
class Database:
    def __init__(self, dsn: str):
        self.dsn = dsn
        self._pool = None

    async def connect(self):
        # Inicializar pool de conexiones
        pass

    async def disconnect(self):
        # Cerrar pool
        pass

    async def fetch(self, query: str) -> list[dict]:
        return []

# Factory async para el servicio
async def get_database() -> Database:
    db = Database("postgresql://localhost/db")
    await db.connect()
    return db

# Servicio de usuarios
class UserService:
    def __init__(self, db: Database):
        self.db = db

    async def get_user(self, user_id: UUID) -> User:
        data = await self.db.fetch(f"SELECT * FROM users WHERE id = {user_id}")
        return User(**data[0])

async def get_user_service(db: Database) -> UserService:
    return UserService(db)

# Configuración de la aplicación con DI
app = Litestar(
    route_handlers=[UserController],
    dependencies={
        "db": Provide(get_database, use_cache=True),  # Singleton por app
        "user_service": Provide(get_user_service),
    },
    lifespan=[database_lifespan],
)

Uso en handlers:

@get("/users/{user_id:uuid}")
async def get_user(
    user_id: UUID,
    user_service: UserService  # Inyectado automáticamente
) -> User:
    return await user_service.get_user(user_id)

Características del DI de Litestar:

  • Resolución por firma de tipo (type hints)
  • Scopes: app (singleton), request
  • Override limpio en tests
  • Soporte para async factories

7. Plugin SQLAlchemy 2.0

uv add sqlalchemy asyncpg

Configuración moderna con SQLAlchemy 2.0:

from litestar.plugins.sqlalchemy import (
    SQLAlchemyPlugin,
    SQLAlchemyAsyncConfig,
)
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import String
from uuid import uuid4

# Base declarativa
class Base(DeclarativeBase):
    pass

# Modelo
class UserModel(Base):
    __tablename__ = "users"

    id: Mapped[str] = mapped_column(primary_key=True, default=lambda: str(uuid4()))
    name: Mapped[str] = mapped_column(String(100))
    email: Mapped[str] = mapped_column(String(255), unique=True)

# Configuración del plugin
sqlalchemy_config = SQLAlchemyAsyncConfig(
    connection_string="postgresql+asyncpg://user:pass@localhost/db",
    create_all=True,
)

sqlalchemy_plugin = SQLAlchemyPlugin(config=sqlalchemy_config)

app = Litestar(
    route_handlers=[UserController],
    plugins=[sqlalchemy_plugin],
)

En los controllers:

from sqlalchemy.ext.asyncio import AsyncSession
from litestar.plugins.sqlalchemy import repository

class UserController(Controller):
    path = "/users"

    @post()
    async def create_user(
        self,
        data: UserCreate,
        db_session: AsyncSession,  # Inyectado por el plugin
    ) -> User:
        user = UserModel(name=data.name, email=data.email)
        db_session.add(user)
        await db_session.commit()
        return User(id=user.id, name=user.name, email=user.email)

8. Middleware integrado y seguridad

CORS configuración

from litestar.middleware.cors import CORSMiddleware
from litestar.config.cors import CORSConfig

cors_config = CORSConfig(
    allow_origins=["https://miapp.com", "https://admin.miapp.com"],
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
    allow_credentials=True,
    max_age=600,
)

app = Litestar(
    route_handlers=[health, UserController],
    middleware=[CORSMiddleware(config=cors_config)],
)

Rate limiting

from litestar.middleware.rate_limit import RateLimitConfig

rate_limit_config = RateLimitConfig(
    rate_limit=("minute", 100),
    exclude=["/health", "/schema"],
)

app = Litestar(
    route_handlers=[...],
    middleware=[rate_limit_config.middleware],
)

Autenticación JWT

from litestar.security.jwt import JWTAuth, JWTToken
from litestar.connection import ASGIConnection
from uuid import UUID

async def retrieve_user_handler(
    token: JWTToken,
    connection: ASGIConnection
) -> User | None:
    return await user_service.get_by_id(UUID(token.sub))

jwt_auth = JWTAuth[
    User,
    Token,
](
    retrieve_user_handler=retrieve_user_handler,
    token_secret="super-secret-key",
    exclude=["/login", "/health"],
)

app = Litestar(
    route_handlers=[...],
    on_app_init=[jwt_auth.on_app_init],
)

9. WebSockets en tiempo real

from litestar import websocket
from litestar.channels import ChannelsPlugin
from litestar.channels.backends.memory import MemoryChannelsBackend

channels_plugin = ChannelsPlugin(
    backend=MemoryChannelsBackend(),
    arbitrary_channels_allowed=True,
)

@websocket("/ws/notifications")
async def notification_handler(socket, channels: ChannelsPlugin) -> None:
    await socket.accept()

    async with channels.start_subscription("notifications") as subscriber:
        async for message in subscriber:
            await socket.send_json({"notification": message})

@post("/notify")
async def send_notification(
    data: dict,
    channels: ChannelsPlugin
) -> dict[str, str]:
    await channels.publish(data["message"], channels=["notifications"])
    return {"status": "sent"}

app = Litestar(
    route_handlers=[notification_handler, send_notification],
    plugins=[channels_plugin],
)

10. Lifecycle Hooks y gestión de recursos

from contextlib import asynccontextmanager
from litestar import Litestar
from collections.abc import AsyncGenerator

@asynccontextmanager
async def database_lifespan(app: Litestar) -> AsyncGenerator[None, None]:
    # Startup
    print("🚀 Iniciando aplicación...")
    db = Database("postgresql://localhost/db")
    await db.connect()
    app.state.db = db

    yield

    # Shutdown
    print("🛑 Cerrando aplicación...")
    await db.disconnect()

# Hooks adicionales
async def startup() -> None:
    print("✅ Servidor iniciado")

async def shutdown() -> None:
    print("⏹️  Servidor detenido")

async def before_request(request) -> None:
    pass

async def after_request(response) -> None:
    return response

app = Litestar(
    route_handlers=[...],
    lifespan=[database_lifespan],
    on_startup=[startup],
    on_shutdown=[shutdown],
    before_request=[before_request],
    after_request=[after_request],
)

11. Testing integrado

import pytest
from litestar.testing import TestClient
from app.main import app

@pytest.fixture
def client() -> TestClient:
    return TestClient(app)

@pytest.fixture
async def async_client() -> TestClient:
    async with TestClient(app) as client:
        yield client

async def test_create_user(async_client: TestClient) -> None:
    response = await async_client.post("/users", json={
        "name": "Juan Pérez",
        "email": "juan@example.com",
        "age": 30
    })

    assert response.status_code == 201
    data = response.json()
    assert data["name"] == "Juan Pérez"
    assert "id" in data

async def test_get_user_not_found(async_client: TestClient) -> None:
    response = await async_client.get("/users/123e4567-e89b-12d3-a456-426614174000")
    assert response.status_code == 404

Ejecutar tests:

uv run pytest -v

12. Docker Production-Ready

Dockerfile:

# Build stage
FROM python:3.13-slim as builder

WORKDIR /app

COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-install-project

# Production stage
FROM python:3.13-slim

WORKDIR /app
COPY --from=builder /bin/uv /bin/uv
COPY --from=builder /app/.venv /app/.venv
COPY app/ ./app/

ENV PATH="/app/.venv/bin:$PATH"
ENV PYTHONPATH="/app"

RUN useradd -m -u 1000 appuser
USER appuser

EXPOSE 8000

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

docker-compose.yml:

version: '3.8'

services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql+asyncpg://postgres:postgres@db:5432/litestar
      - JWT_SECRET=${JWT_SECRET}
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: litestar
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Litestar vs FastAPI: Comparativa

✅ Ventajas de Litestar

AspectoLitestarFastAPI
Rendimiento10-20% más rápido con msgspecMuy bueno con Pydantic v2
IncluidoDI, middlewares, WebSockets, cachingRequiere más extensiones
ArquitecturaControllers + DI nativoFunctions + Depends
Serializaciónmsgspec nativoPydantic principal
DocumentaciónMúltiples UIs (Swagger, ReDoc, Elements)Swagger/ReDoc

❌ Desventajas

  • Ecosistema más pequeño: Menos tutoriales y Stack Overflow (no nos importa, para eso tenemos la IA?)
  • Curva de aprendizaje: Más conceptos que entender (DI, scopes, plugins)
  • Menor adopción: Menos ofertas de trabajo específicas

¿Cuándo usar Litestar?

✅ Ideal para:

  • APIs de alto throughput: >10k req/s
  • Microservicios: Arquitectura limpia y testeable
  • Proyectos enterprise: Necesitas estructura y DI sólido
  • Equipos experimentados: Valoran arquitectura sobre velocidad inicial

❌ No ideal para:

  • MVP rápidos: FastAPI tiene más ejemplos y es más rápido de empezar
  • Equipos junior: Menor comunidad para consultar
  • Integraciones específicas: Algunas librerías tienen mejor soporte para FastAPI

Resumen: Comandos esenciales

# Desarrollo
uv run litestar run --reload
uv run pytest -v
uv run ruff check .
uv run mypy app/

# Producción
docker-compose up --build
uv run uvicorn app.main:app --workers 4

Recursos adicionales


Última actualización: Febrero 2026 | Litestar 2.20+