FastAPI para automatizaciones internas: cuándo elegir Python en vez de Spring Boot

Cuándo tiene sentido usar FastAPI para herramientas internas y cuándo es mejor Spring Boot. Criterios técnicos reales.

Cover for FastAPI para automatizaciones internas: cuándo elegir Python en vez de Spring Boot
Actualizado: 18 may 2026

Hace un par de años me pidieron montar una herramienta interna para que el equipo de datos pudiera lanzar procesos de scraping bajo demanda. Necesitaba una API REST sencilla, integración con scripts Python que ya existían y un panel mínimo para ver el estado de las ejecuciones. Mi primer impulso fue montar un Spring Boot. Mi segundo impulso, después de pensar diez minutos, fue abrir un fichero main.py y tener un endpoint funcionando en veinte líneas con FastAPI.

Esa decisión me ahorró días de trabajo. Pero no siempre es así. He tenido otros casos donde empecé con FastAPI y terminé deseando tener la estructura de Spring Boot. La diferencia entre una buena y una mala decisión técnica aquí no es qué framework es “mejor”, sino entender qué necesitas realmente y dónde va a doler cada opción.


El contexto importa más que el framework

Antes de comparar features, hay que definir qué tipo de herramienta estás construyendo. No es lo mismo un microservicio que va a recibir tráfico de producción que un endpoint interno que usan tres personas del equipo para lanzar scripts.

Las herramientas internas suelen tener estas características:

  • Pocos usuarios (equipo técnico, operaciones, datos)
  • Requisitos que cambian rápido (“ahora necesitamos también exportar a CSV”)
  • Integración con scripts existentes, notebooks o pipelines de datos
  • Prioridad en velocidad de desarrollo sobre escalabilidad
  • Ciclo de vida corto o incierto (puede que se use tres meses y se descarte)

En ese contexto, las prioridades técnicas son diferentes a las de un servicio de producción. Y ahí es donde FastAPI y Spring Boot se diferencian de verdad.


FastAPI: cuándo tiene sentido

FastAPI brilla cuando necesitas una API HTTP rápida de montar que se integre con el ecosistema Python. Su diseño está pensado para exactamente eso: definir endpoints con tipado, generar documentación automática y arrancar con el mínimo de configuración.

Un ejemplo real. Necesitaba un servicio interno que recibiera una URL, lanzara un proceso de scraping con httpx y BeautifulSoup, y devolviera el resultado estructurado:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, HttpUrl
import httpx
from bs4 import BeautifulSoup

app = FastAPI(title="Scraping Service", version="0.1.0")

class ScrapeRequest(BaseModel):
    url: HttpUrl
    selector: str = "article"

class ScrapeResult(BaseModel):
    url: str
    title: str | None
    content: str
    word_count: int

@app.post("/scrape", response_model=ScrapeResult)
async def scrape(request: ScrapeRequest):
    async with httpx.AsyncClient(timeout=30) as client:
        response = await client.get(str(request.url))
        if response.status_code != 200:
            raise HTTPException(
                status_code=502,
                detail=f"Target returned {response.status_code}"
            )

    soup = BeautifulSoup(response.text, "html.parser")
    element = soup.select_one(request.selector)

    if not element:
        raise HTTPException(status_code=404, detail="Selector not found")

    text = element.get_text(strip=True)
    return ScrapeResult(
        url=str(request.url),
        title=soup.title.string if soup.title else None,
        content=text[:5000],
        word_count=len(text.split())
    )

Eso es un servicio funcional. Con uvicorn main:app --reload lo tengo corriendo en segundos. La documentación interactiva está en /docs sin configurar nada. Pydantic valida la entrada y serializa la salida. Si necesito añadir otro endpoint, son diez líneas más.

Montar esto en Spring Boot no es difícil, pero necesito un proyecto con estructura, dependencias, configuración de Jackson, una clase de request, una de response, un controlador, posiblemente un servicio. No es que sea complicado, es que es más ceremonia para el mismo resultado en un contexto donde la ceremonia no aporta.


Spring Boot: cuándo sigue siendo mejor opción

FastAPI tiene sus límites. Y esos límites aparecen cuando el proyecto crece o cuando los requisitos son los de un servicio de producción real.

Caso concreto: un servicio interno que empezó como “una API para consultar configuraciones” y fue creciendo hasta tener autenticación, acceso a base de datos, lógica de negocio compleja, transacciones, colas de mensajes y un ciclo de vida de varios años. Eso lo empecé en FastAPI y llegó un punto donde echaba de menos la inyección de dependencias de Spring, las transacciones declarativas, la integración con Kafka y la estructura que el framework te obliga a tener.

Spring Boot es mejor opción cuando:

  • El servicio va a crecer y necesita estructura desde el principio
  • Hay lógica de negocio compleja con transacciones
  • El equipo ya trabaja con JVM y el servicio debe integrarse con otros servicios Java/Kotlin
  • Necesitas un ecosistema maduro de librerías enterprise (seguridad, mensajería, batch)
  • El ciclo de vida es largo y la mantenibilidad importa más que la velocidad inicial

Tipado: Pydantic vs Kotlin type-safe

Una de las cosas que más me gustan de FastAPI es Pydantic. El sistema de validación es potente, expresivo y se integra de forma natural con el framework:

from pydantic import BaseModel, Field, field_validator
from datetime import datetime
from enum import Enum

class Priority(str, Enum):
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"

class TaskCreate(BaseModel):
    name: str = Field(min_length=1, max_length=200)
    description: str | None = None
    priority: Priority = Priority.MEDIUM
    due_date: datetime | None = None

    @field_validator("due_date")
    @classmethod
    def due_date_must_be_future(cls, v):
        if v and v < datetime.now():
            raise ValueError("due_date must be in the future")
        return v

En Kotlin con Spring Boot, el equivalente sería algo como:

data class TaskCreate(
    @field:NotBlank
    @field:Size(max = 200)
    val name: String,

    val description: String? = null,

    val priority: Priority = Priority.MEDIUM,

    @field:Future
    val dueDate: LocalDateTime? = null
)

enum class Priority { LOW, MEDIUM, HIGH }

Ambos enfoques son válidos. La diferencia está en la filosofía:

AspectoPydantic (FastAPI)Bean Validation (Spring)
ValidaciónEn el modelo, con validators customAnotaciones + validator classes
SerializaciónIntegrada en el modeloJackson separado
Coerción de tiposAutomática (str “3” -> int 3)Estricta por defecto
DocumentaciónGenera JSON Schema automáticoNecesita SpringDoc/Swagger
Runtime vs compilaciónRuntime (Python)Mix: compilación (Kotlin) + runtime (annotations)

Pydantic es más flexible para prototipos rápidos. El sistema de tipos de Kotlin es más seguro a largo plazo. No es que uno sea mejor que otro; sirven para contextos diferentes.


Mantenibilidad: donde se nota la diferencia a largo plazo

Aquí es donde tengo que ser honesto con Python. Un proyecto FastAPI de 500 líneas es un placer. Un proyecto FastAPI de 15.000 líneas empieza a necesitar mucha disciplina que el lenguaje no te obliga a tener.

Python con type hints ha mejorado enormemente, pero los tipos son opcionales. Puedes escribir un servicio entero sin type hints y funciona igual. En un equipo de dos personas que mantienen una herramienta interna, eso no es un problema. En un equipo de ocho personas que mantienen un servicio en producción durante tres años, los type hints opcionales significan que parte del código los tendrá y parte no, y el linter te avisa pero no te obliga.

En Kotlin, el compilador te obliga. No puedes pasar un null donde se espera un String. No puedes ignorar un tipo de retorno sin hacer un cast explícito. Esa rigidez cuesta al principio pero paga dividendos cuando el proyecto crece.

Mi regla personal:

Si el proyecto va a durar menos de seis meses y lo mantiene el mismo equipo que lo construyó, FastAPI. Si va a durar más o va a rotar el equipo, Spring Boot con Kotlin.

No es una regla absoluta, pero me ha funcionado como heurística.


Integración con scripts y herramientas de datos

Este es uno de los puntos fuertes de FastAPI. Si tu organización tiene scripts Python para procesamiento de datos, notebooks de Jupyter, pipelines con pandas o modelos de ML, un servicio FastAPI se integra de forma natural:

from fastapi import FastAPI, BackgroundTasks
from app.pipelines import run_daily_report
from app.models import ReportConfig

app = FastAPI()

@app.post("/reports/generate")
async def generate_report(
    config: ReportConfig,
    background_tasks: BackgroundTasks
):
    background_tasks.add_task(run_daily_report, config)
    return {"status": "queued", "report_type": config.report_type}

Ese run_daily_report puede ser una función que ya existía, que usa pandas, que llama a APIs externas con requests, que genera un CSV y lo sube a S3. No necesitas adaptarla, solo la importas.

En Spring Boot, integrar con código Python requiere llamadas a proceso externo, APIs REST intermedias o algo como GraalPython. Es factible pero no es natural.

Esto no es un argumento para usar FastAPI siempre. Es un argumento para usarlo cuando el ecosistema alrededor es Python. Si el ecosistema es JVM, Spring Boot es la elección natural por la misma razón.


Deploy: las diferencias prácticas

Desplegar un servicio FastAPI y uno Spring Boot tiene diferencias reales que afectan a la operación diaria.

FastAPI con Docker:

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

Imagen resultante: ~150MB. Tiempo de arranque: 1-2 segundos. Memoria en reposo: ~50MB.

Spring Boot con Docker:

FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY build/libs/app.jar .
CMD ["java", "-jar", "app.jar"]

Imagen resultante: ~200-300MB. Tiempo de arranque: 5-15 segundos (sin GraalVM). Memoria en reposo: ~150-300MB.

Para herramientas internas que se levantan y bajan con frecuencia, o que corren en serverless (Lambda, Cloud Run), la diferencia de arranque y memoria es relevante. FastAPI es más ligero. Con GraalVM native image puedes compilar Spring Boot a un binario nativo que arranca en milisegundos, pero la compilación nativa tiene sus propios problemas (tiempo de build, compatibilidad con librerías, reflection).

CriterioFastAPISpring BootSpring Boot + GraalVM
Imagen Docker~150MB~250MB~100MB
Arranque1-2s5-15smenos de 1s
Memoria reposo~50MB~200MB~50MB
Tiempo de buildSegundos30s-2min5-15min
ServerlessNaturalPosibleBueno
Ecosistema libreríaspip installMaven/GradleLimitado (reflection)

Tabla comparativa por escenario

Después de usar ambos en diferentes contextos, esta es mi guía de decisión:

EscenarioRecomendaciónPor qué
API interna para lanzar scripts PythonFastAPIIntegración directa, sin fricción
Microservicio con lógica de negocio complejaSpring BootEstructura, transacciones, DI
Wrapper de modelo MLFastAPIEcosistema Python, performance async
Servicio que se integra con Kafka/RabbitMQSpring BootSpring Cloud Stream, madurez
Herramienta de datos para equipo de analyticsFastAPIPandas, notebooks, ecosystem
Servicio con autenticación enterprise (LDAP, OAuth)Spring BootSpring Security, madurez
Prototipo rápido para validar una ideaFastAPIVelocidad de desarrollo
Servicio que va a durar 3+ añosSpring BootMantenibilidad, tipado, estructura
CRUD simple con pocos endpointsFastAPIMenos ceremonia
Orquestación de workflows complejosDepende del equipoPython si ya hay infra Python, JVM si ya hay infra JVM

Lo que he aprendido usándolos en paralelo

Uso ambos frameworks en mi día a día. Spring Boot con Kotlin para los servicios principales y FastAPI para herramientas auxiliares, prototipos y todo lo que toca el ecosistema de datos. Esta convivencia me ha enseñado un par de cosas:

La primera es que la elección del framework importa menos de lo que la gente discute en Twitter. Lo que importa es que la herramienta encaje con el contexto: el equipo, el ecosistema, el ciclo de vida del proyecto y los requisitos reales (no los imaginados).

La segunda es que cambiar de framework tiene un coste real. No solo técnico, sino cognitivo. Cada vez que saltas de Kotlin a Python cambias de modelo mental: mutabilidad, tipado, manejo de errores, testing. Si tu equipo hace ese salto constantemente, pierdes la fluidez que ganas con la especialización.

La tercera es que la mejor herramienta interna es la que se construye rápido, se usa de verdad y se puede tirar sin drama cuando deja de ser útil. Si elegir Spring Boot para una herramienta que va a vivir tres meses significa que tardas una semana más en tenerla lista, has optimizado para el criterio equivocado.

No elijas framework por preferencia personal ni por la tendencia del momento. Elige por el contexto concreto del problema que vas a resolver. Y si no estás seguro, empieza con lo más simple que pueda funcionar. Siempre puedes migrar después, pero rara vez necesitas hacerlo.

OshyTech

Ingeniería backend y de datos orientada a sistemas escalables, automatización e IA.

Navegación

Copyright 2026 OshyTech. Todos los derechos reservados