Day 13: Serving it: FastAPI endpoint on the droplet
From CLI tool to service
You already know REST APIs cold from NestJS. FastAPI's job today is narrow: wrap yesterday's process_image in one endpoint, deployed on the same droplet as the rest of the stack, callable from a Next.js page. Focus on what's actually new, not on relearning what an endpoint is.
What's actually different from NestJS
- Route handlers are plain functions (often
async def), not classes/decorators-on-methods - Request/response shapes are declared with Pydantic models — FastAPI's rough equivalent of a NestJS DTO + class-validator, but it also generates OpenAPI docs from them automatically
- File uploads use
UploadFile, an async-friendly wrapper around the incoming multipart data - This service is stateless and CPU-only for now — no need for the async DB/queue patterns you'd reach for in NestJS at this stage
from fastapi import FastAPI, UploadFile, HTTPException
from fastapi.responses import Response
import cv2
import numpy as np
from .pipeline import remove_background, resize_and_normalize
app = FastAPI(title="FitXpert Catalog Tool")
@app.post("/catalog/clean")
async def clean_garment_photo(file: UploadFile, size: int = 1024) -> Response:
if file.content_type not in ("image/jpeg", "image/png"):
raise HTTPException(400, "Expected a JPEG or PNG image")
raw = await file.read()
img = cv2.imdecode(np.frombuffer(raw, np.uint8), cv2.IMREAD_COLOR)
if img is None:
raise HTTPException(400, "Could not decode image")
cutout = remove_background(img)
result = resize_and_normalize(cutout, size)
ok, encoded = cv2.imencode(".png", result)
return Response(content=encoded.tobytes(), media_type="image/png")
@app.get("/health")
async def health():
return {"status": "ok"}uv run uvicorn catalog.api:app --host 0.0.0.0 --port 8001
# smoke test
curl -X POST -F "file=@garment.jpg" http://localhost:8001/catalog/clean -o cleaned.pngCalling it from Next.js
async function cleanGarmentPhoto(file: File): Promise<Blob> {
const formData = new FormData();
formData.append('file', file);
const res = await fetch(process.env.CATALOG_SERVICE_URL + '/catalog/clean', {
method: 'POST',
body: formData,
});
if (!res.ok) throw new Error(`Catalog service error: ${res.status}`);
return res.blob();
}The shape that repeats through Stage 6
NestJS/Next.js stays the gateway and UI; a Python (FastAPI) service does the actual inference work behind it. Today's tiny CPU-only endpoint is architecturally identical to Stage 1's classifier service, Stage 2's measurement engine, and Stage 3's stylist — only the model inside changes.
Key terms
- Pydantic model
- FastAPI's data-validation/serialization class — declares request/response shapes and auto-generates OpenAPI docs from them.
- UploadFile
- FastAPI's async-friendly wrapper for an incoming multipart file upload.
- uvicorn
- An ASGI server that runs FastAPI (and other async Python web) applications.
In this roadmap's architecture, what role does FastAPI play relative to NestJS/Next.js?