typestar

api_service.py en Python

Un servicio FastAPI pequeño: modelos, store inyectado como dependencia, rutas y pruebas.

from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field


class TourIn(BaseModel):
    slug: str = Field(pattern=r"^[a-z][a-z0-9_-]{1,31}$")
    title: str = Field(max_length=60)
    steps: int = Field(default=0, ge=0)


class TourOut(TourIn):
    id: int


class Almacen:
    """Un sustituto en memoria de una base de datos."""

    def __init__(self):
        self._rows: dict[int, TourOut] = {}
        self._next = 1

    def add(self, tour: TourIn) -> TourOut:
        if any(row.slug == tour.slug for row in self._rows.values()):
            raise KeyError(tour.slug)
        row = TourOut(id=self._next, **tour.model_dump())
        self._rows[self._next] = row
        self._next += 1
        return row

    def all(self) -> list[TourOut]:
        return list(self._rows.values())

    def get(self, tour_id: int) -> TourOut | None:
        return self._rows.get(tour_id)


almacen = Almacen()
app = FastAPI(title="tours")


def get_store() -> Almacen:
    return almacen


@app.get("/healthz")
def healthz():
    return {"status": "ok"}


@app.get("/tours", response_model=list[TourOut])
def list_tours(shop: Almacen = Depends(get_store)):
    return shop.all()


@app.post("/tours", response_model=TourOut,
          status_code=status.HTTP_201_CREATED)
def create_tour(tour: TourIn, shop: Almacen = Depends(get_store)):
    try:
        return shop.add(tour)
    except KeyError:
        raise HTTPException(status.HTTP_409_CONFLICT, f"{tour.slug} ya existe")


@app.get("/tours/{tour_id}", response_model=TourOut)
def read_tour(tour_id: int, shop: Almacen = Depends(get_store)):
    found = shop.get(tour_id)
    if found is None:
        raise HTTPException(status.HTTP_404_NOT_FOUND, "no existe ese tour")
    return found


def principal():
    cliente = TestClient(app)
    print(cliente.get("/healthz").json())

    creado = cliente.post("/tours", json={"slug": "basics",
                                       "title": "Fundamentos del lenguaje",
                                       "steps": 60})
    print(creado.status_code, creado.json())

    otra = cliente.post("/tours", json={"slug": "basics", "title": "Copia"})
    print(otra.status_code, otra.json()["detail"])

    malo = cliente.post("/tours", json={"slug": "Slug Malo", "title": "x"})
    print(malo.status_code, malo.json()["detail"][0]["loc"])

    print(cliente.get("/tours/1").json()["title"])
    print(cliente.get("/tours/99").status_code)
    print(len(cliente.get("/tours").json()))


if __name__ == "__main__":
    principal()

Cómo funciona

  1. Los modelos de Pydantic definen las formas de petición y respuesta.
  2. El store llega vía Depends, así una prueba puede reemplazarlo.
  3. El TestClient ejercita cada ruta al final del archivo.

Palabras clave y builtins usados aquí

El intento, en números

Líneas
94
Caracteres a escribir
2292
Tokens
668
Ritmo de tres estrellas
115 tpm

Al ritmo de tres estrellas de 115 tokens por minuto, este intento toma unos 349 segundos.

Escribe este fragmento

Paso 1 de 2 en Bis; paso 18 de 19 en Servicios web y acceso a datos.

← Anterior Siguiente →