FastAPI per a automatitzacions internes: quan triar Python en lloc de Spring Boot

Quan té sentit fer servir FastAPI per a eines internes i quan és millor Spring Boot. Criteris tècnics reals.

Cover for FastAPI per a automatitzacions internes: quan triar Python en lloc de Spring Boot

Fa un parell d’anys em van demanar muntar una eina interna perquè l’equip de dades pogués llançar processos de scraping sota demanda. Necessitava una API REST senzilla, integració amb scripts Python que ja existien i un panell mínim per veure l’estat de les execucions. El meu primer impuls va ser muntar un Spring Boot. El meu segon impuls, després de pensar deu minuts, va ser obrir un fitxer main.py i tenir un endpoint funcionant en vint línies amb FastAPI.

Aquella decisió em va estalviar dies de feina. Però no sempre és així. He tingut altres casos on vaig començar amb FastAPI i vaig acabar desitjant tenir l’estructura de Spring Boot. La diferència entre una bona i una mala decisió tècnica aquí no és quin framework és “millor”, sinó entendre què necessites realment i on farà mal cada opció.


El context importa més que el framework

Abans de comparar features, cal definir quin tipus d’eina estàs construint. No és el mateix un microservei que rebrà tràfic de producció que un endpoint intern que fan servir tres persones de l’equip per llançar scripts.

Les eines internes solen tenir aquestes característiques:

  • Pocs usuaris (equip tècnic, operacions, dades)
  • Requisits que canvien ràpid (“ara necessitem també exportar a CSV”)
  • Integració amb scripts existents, notebooks o pipelines de dades
  • Prioritat en velocitat de desenvolupament sobre escalabilitat
  • Cicle de vida curt o incert (pot ser que es faci servir tres mesos i es descarti)

En aquest context, les prioritats tècniques són diferents de les d’un servei de producció. I és aquí on FastAPI i Spring Boot es diferencien de veritat.


FastAPI: quan té sentit

FastAPI brilla quan necessites una API HTTP ràpida de muntar que s’integri amb l’ecosistema Python. El seu disseny està pensat exactament per això: definir endpoints amb tipat, generar documentació automàtica i arrencar amb el mínim de configuració.

Un exemple real. Necessitava un servei intern que rebés una URL, llancés un procés de scraping amb httpx i BeautifulSoup, i retornés el resultat estructurat:

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())
    )

Això és un servei funcional. Amb uvicorn main:app --reload el tinc corrent en segons. La documentació interactiva és a /docs sense configurar res. Pydantic valida l’entrada i serialitza la sortida. Si necessito afegir un altre endpoint, són deu línies més.

Muntar això a Spring Boot no és difícil, però necessito un projecte amb estructura, dependències, configuració de Jackson, una classe de request, una de response, un controlador, possiblement un servei. No és que sigui complicat, és que és més cerimònia pel mateix resultat en un context on la cerimònia no aporta.


Spring Boot: quan continua sent millor opció

FastAPI té els seus límits. I aquests límits apareixen quan el projecte creix o quan els requisits són els d’un servei de producció real.

Cas concret: un servei intern que va començar com “una API per consultar configuracions” i va anar creixent fins a tenir autenticació, accés a base de dades, lògica de negoci complexa, transaccions, cues de missatges i un cicle de vida de diversos anys. Això ho vaig començar amb FastAPI i va arribar un punt on trobava a faltar la injecció de dependències de Spring, les transaccions declaratives, la integració amb Kafka i l’estructura que el framework t’obliga a tenir.

Spring Boot és millor opció quan:

  • El servei creixerà i necessita estructura des del principi
  • Hi ha lògica de negoci complexa amb transaccions
  • L’equip ja treballa amb JVM i el servei s’ha d’integrar amb altres serveis Java/Kotlin
  • Necessites un ecosistema madur de llibreries enterprise (seguretat, missatgeria, batch)
  • El cicle de vida és llarg i la mantenibilitat importa més que la velocitat inicial

Tipat: Pydantic vs Kotlin type-safe

Una de les coses que més m’agraden de FastAPI és Pydantic. El sistema de validació és potent, expressiu i s’integra de forma natural amb 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

A Kotlin amb Spring Boot, l’equivalent seria alguna cosa com:

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 }

Ambdós enfocaments són vàlids. La diferència està en la filosofia:

AspectePydantic (FastAPI)Bean Validation (Spring)
ValidacióAl model, amb validators customAnotacions + validator classes
SerialitzacióIntegrada al modelJackson separat
Coerció de tipusAutomàtica (str “3” -> int 3)Estricta per defecte
DocumentacióGenera JSON Schema automàticNecessita SpringDoc/Swagger
Runtime vs compilacióRuntime (Python)Mix: compilació (Kotlin) + runtime (annotations)

Pydantic és més flexible per a prototips ràpids. El sistema de tipus de Kotlin és més segur a llarg termini. No és que un sigui millor que l’altre; serveixen per a contextos diferents.


Mantenibilitat: on es nota la diferència a llarg termini

Aquí és on he de ser honest amb Python. Un projecte FastAPI de 500 línies és un plaer. Un projecte FastAPI de 15.000 línies comença a necessitar molta disciplina que el llenguatge no t’obliga a tenir.

Python amb type hints ha millorat enormement, però els tipus són opcionals. Pots escriure un servei sencer sense type hints i funciona igual. En un equip de dues persones que mantenen una eina interna, això no és un problema. En un equip de vuit persones que mantenen un servei en producció durant tres anys, els type hints opcionals volen dir que part del codi els tindrà i part no, i el linter t’avisa però no t’obliga.

A Kotlin, el compilador t’obliga. No pots passar un null on s’espera un String. No pots ignorar un tipus de retorn sense fer un cast explícit. Aquesta rigidesa costa al principi però paga dividends quan el projecte creix.

La meva regla personal:

Si el projecte durarà menys de sis mesos i el manté el mateix equip que el va construir, FastAPI. Si durarà més o rotarà l’equip, Spring Boot amb Kotlin.

No és una regla absoluta, però m’ha funcionat com a heurística.


Integració amb scripts i eines de dades

Aquest és un dels punts forts de FastAPI. Si la teva organització té scripts Python per a processament de dades, notebooks de Jupyter, pipelines amb pandas o models de ML, un servei FastAPI s’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}

Aquell run_daily_report pot ser una funció que ja existia, que fa servir pandas, que crida a APIs externes amb requests, que genera un CSV i el puja a S3. No necessites adaptar-la, només la importes.

A Spring Boot, integrar amb codi Python requereix crides a procés extern, APIs REST intermèdies o alguna cosa com GraalPython. És factible però no és natural.

Això no és un argument per fer servir FastAPI sempre. És un argument per fer-lo servir quan l’ecosistema al voltant és Python. Si l’ecosistema és JVM, Spring Boot és l’elecció natural per la mateixa raó.


Deploy: les diferències pràctiques

Desplegar un servei FastAPI i un Spring Boot té diferències reals que afecten l’operació diària.

FastAPI amb 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"]

Imatge resultant: ~150MB. Temps d’arrencada: 1-2 segons. Memòria en repòs: ~50MB.

Spring Boot amb Docker:

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

Imatge resultant: ~200-300MB. Temps d’arrencada: 5-15 segons (sense GraalVM). Memòria en repòs: ~150-300MB.

Per a eines internes que s’aixequen i es baixen amb freqüència, o que corren en serverless (Lambda, Cloud Run), la diferència d’arrencada i memòria és rellevant. FastAPI és més lleuger. Amb GraalVM native image pots compilar Spring Boot a un binari natiu que arrenca en mil·lisegons, però la compilació nativa té els seus propis problemes (temps de build, compatibilitat amb llibreries, reflection).

CriteriFastAPISpring BootSpring Boot + GraalVM
Imatge Docker~150MB~250MB~100MB
Arrencada1-2s5-15smenys d’1s
Memòria repòs~50MB~200MB~50MB
Temps de buildSegons30s-2min5-15min
ServerlessNaturalPossibleBo
Ecosistema llibreriespip installMaven/GradleLimitat (reflection)

Taula comparativa per escenari

Després de fer servir ambdós en diferents contextos, aquesta és la meva guia de decisió:

EscenariRecomanacióPer què
API interna per llançar scripts PythonFastAPIIntegració directa, sense fricció
Microservei amb lògica de negoci complexaSpring BootEstructura, transaccions, DI
Wrapper de model MLFastAPIEcosistema Python, performance async
Servei que s’integra amb Kafka/RabbitMQSpring BootSpring Cloud Stream, maduresa
Eina de dades per a equip d’analyticsFastAPIPandas, notebooks, ecosystem
Servei amb autenticació enterprise (LDAP, OAuth)Spring BootSpring Security, maduresa
Prototip ràpid per validar una ideaFastAPIVelocitat de desenvolupament
Servei que durarà 3+ anysSpring BootMantenibilitat, tipat, estructura
CRUD simple amb pocs endpointsFastAPIMenys cerimònia
Orquestració de workflows complexosDepèn de l’equipPython si ja hi ha infra Python, JVM si ja hi ha infra JVM

El que he après fent-los servir en paral·lel

Faig servir ambdós frameworks al meu dia a dia. Spring Boot amb Kotlin per als serveis principals i FastAPI per a eines auxiliars, prototips i tot el que toca l’ecosistema de dades. Aquesta convivència m’ha ensenyat un parell de coses:

La primera és que l’elecció del framework importa menys del que la gent discuteix a Twitter. El que importa és que l’eina encaixi amb el context: l’equip, l’ecosistema, el cicle de vida del projecte i els requisits reals (no els imaginats).

La segona és que canviar de framework té un cost real. No només tècnic, sinó cognitiu. Cada vegada que saltes de Kotlin a Python canvies de model mental: mutabilitat, tipat, gestió d’errors, testing. Si el teu equip fa aquest salt constantment, perds la fluïdesa que guanyes amb l’especialització.

La tercera és que la millor eina interna és la que es construeix ràpid, es fa servir de veritat i es pot llençar sense drama quan deixa de ser útil. Si triar Spring Boot per a una eina que viurà tres mesos vol dir que trigues una setmana més a tenir-la llesta, has optimitzat pel criteri equivocat.

No triïs framework per preferència personal ni per la tendència del moment. Tria pel context concret del problema que resoldràs. I si no n’estàs segur, comença pel més simple que pugui funcionar. Sempre pots migrar després, però rarament necessites fer-ho.

OshyTech

Enginyeria backend i de dades orientada a sistemes escalables, automatització i IA.

Navegació

Copyright 2026 OshyTech. Tots els drets reservats