Documentation Index Fetch the complete documentation index at: https://mintlify.com/jonatan-leal/ia-proyecto-sustituto/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Phase 3 provides a FastAPI-based REST API for real-time diabetes predictions. This guide covers deployment strategies, production considerations, and integration patterns.
Framework : FastAPI 0.111.0Deployment : Docker containerizedBest For : Web apps, mobile apps, microservices, real-time predictions
API Architecture
fase-3/
├── apirest.py # Main FastAPI application
├── patient.py # Pydantic Patient model
├── train.py # Training logic
├── predict.py # Prediction logic
├── Dockerfile # Container configuration
└── requirements.txt # Python dependencies
Endpoints
POST /train Trains a new model from train.csv Input : None (reads train.csv)Output : JSON status message
POST /predict Predicts diabetes for a single patient Input : Patient JSONOutput : Prediction message
Quick Deployment
Build Image
cd ~/workspace/source/fase-3
docker build -t diabetes-api .
Start API Server
docker run -d \
--name diabetes-api \
-p 80:80 \
diabetes-api
API is now accessible at http://localhost
Copy Training Data
docker cp train.csv diabetes-api:/app
Train Model via API
curl -X POST http://localhost/train
Response: { "message" : "Model successfully trained" }
Test Prediction
curl -X POST http://localhost/predict \
-H "Content-Type: application/json" \
-d '{
"gender": "Female",
"age": 36,
"hypertension": 0,
"heart_disease": 0,
"smoking_history": "current",
"bmi": 32.27,
"HbA1c_level": 6.2,
"blood_glucose_level": 220
}'
Response: { "message" : "Tiene diabetes" }
Production Deployment
Environment Configuration
Use environment variables for configuration:
docker run -d \
--name diabetes-api \
-p 80:80 \
-e MODEL_FILE=/app/models/model.pkl \
-e DATA_FILE=/app/data/train.csv \
-e LOG_LEVEL=INFO \
diabetes-api
Update Python code to use env vars:
import os
# train.py
model_file = os.getenv( "MODEL_FILE" , "model.pkl" )
data_file = os.getenv( "DATA_FILE" , "train.csv" )
# predict.py
model_file = os.getenv( "MODEL_FILE" , "model.pkl" )
Persistent Storage
Use volumes to persist models and data:
mkdir -p ./data ./models
docker run -d \
--name diabetes-api \
-p 80:80 \
-v $( pwd ) /data:/app/data \
-v $( pwd ) /models:/app/models \
-e MODEL_FILE=/app/models/model.pkl \
-e DATA_FILE=/app/data/train.csv \
diabetes-api
Benefits:
Models persist across container restarts
Easy model updates without rebuilding image
Backup models from host filesystem
Reverse Proxy with Nginx
For production, use Nginx as reverse proxy:
docker-compose.yml:
version : '3.8'
services :
api :
build : ./fase-3
container_name : diabetes-api
expose :
- "80"
volumes :
- ./data:/app/data
- ./models:/app/models
environment :
- MODEL_FILE=/app/models/model.pkl
- DATA_FILE=/app/data/train.csv
restart : unless-stopped
nginx :
image : nginx:alpine
container_name : diabetes-nginx
ports :
- "80:80"
- "443:443"
volumes :
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on :
- api
restart : unless-stopped
nginx.conf:
http {
upstream diabetes_api {
server api:80;
}
server {
listen 80 ;
server_name your-domain.com;
location / {
proxy_pass http://diabetes_api;
proxy_set_header Host $ host ;
proxy_set_header X-Real-IP $ remote_addr ;
proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for ;
proxy_set_header X-Forwarded-Proto $ scheme ;
}
}
# HTTPS configuration
server {
listen 443 ssl;
server_name your-domain.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
location / {
proxy_pass http://diabetes_api;
proxy_set_header Host $ host ;
proxy_set_header X-Real-IP $ remote_addr ;
proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for ;
proxy_set_header X-Forwarded-Proto $ scheme ;
}
}
}
Health Checks
Add health check endpoint to apirest.py:
from fastapi import FastAPI
import os
app = FastAPI()
@app.get ( "/health" )
def health_check ():
model_exists = os.path.isfile( "model.pkl" )
return {
"status" : "healthy" if model_exists else "degraded" ,
"model_loaded" : model_exists
}
@app.get ( "/ready" )
def readiness_check ():
# Check if model is trained and ready
if os.path.isfile( "model.pkl" ):
return { "ready" : True }
return { "ready" : False }, 503
Configure Docker health check:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:80/health || exit 1
Monitoring and Logging
Structured Logging:
from loguru import logger
import sys
# Configure logger
logger.remove() # Remove default handler
logger.add(
sys.stdout,
format = " {time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {message} " ,
level = "INFO"
)
logger.add(
"/app/logs/api.log" ,
rotation = "100 MB" ,
retention = "30 days" ,
level = "INFO"
)
# Use in endpoints
@app.post ( "/predict" )
def predict ( patient : Patient):
logger.info( f "Prediction request: { patient.dict() } " )
result = pr.predict(patient)
logger.info( f "Prediction result: { result } " )
return result
Prometheus Metrics:
from prometheus_client import Counter, Histogram, make_asgi_app
import time
# Metrics
prediction_counter = Counter(
'predictions_total' ,
'Total number of predictions' ,
[ 'outcome' ]
)
prediction_duration = Histogram(
'prediction_duration_seconds' ,
'Time spent processing prediction'
)
@app.post ( "/predict" )
def predict ( patient : Patient):
start_time = time.time()
result = pr.predict(patient)
# Record metrics
outcome = "diabetes" if "Tiene diabetes" in result[ "message" ] else "no_diabetes"
prediction_counter.labels( outcome = outcome).inc()
prediction_duration.observe(time.time() - start_time)
return result
# Mount Prometheus metrics endpoint
metrics_app = make_asgi_app()
app.mount( "/metrics" , metrics_app)
Cloud Deployment
AWS ECS (Elastic Container Service)
Push to ECR
# Authenticate
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin < account-i d > .dkr.ecr.us-east-1.amazonaws.com
# Tag image
docker tag diabetes-api:latest < account-i d > .dkr.ecr.us-east-1.amazonaws.com/diabetes-api:latest
# Push
docker push < account-i d > .dkr.ecr.us-east-1.amazonaws.com/diabetes-api:latest
Create ECS Task Definition
{
"family" : "diabetes-api" ,
"containerDefinitions" : [
{
"name" : "diabetes-api" ,
"image" : "<account-id>.dkr.ecr.us-east-1.amazonaws.com/diabetes-api:latest" ,
"portMappings" : [
{
"containerPort" : 80 ,
"protocol" : "tcp"
}
],
"environment" : [
{ "name" : "MODEL_FILE" , "value" : "/app/models/model.pkl" },
{ "name" : "DATA_FILE" , "value" : "/app/data/train.csv" }
],
"mountPoints" : [
{
"sourceVolume" : "efs-storage" ,
"containerPath" : "/app/models"
}
]
}
],
"volumes" : [
{
"name" : "efs-storage" ,
"efsVolumeConfiguration" : {
"fileSystemId" : "fs-12345678"
}
}
]
}
Create ECS Service
aws ecs create-service \
--cluster diabetes-cluster \
--service-name diabetes-api-service \
--task-definition diabetes-api \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-12345],securityGroups=[sg-12345],assignPublicIp=ENABLED}"
Google Cloud Run
Build and Push
# Configure gcloud
gcloud auth configure-docker
# Tag and push
docker tag diabetes-api gcr.io/your-project/diabetes-api
docker push gcr.io/your-project/diabetes-api
Deploy to Cloud Run
gcloud run deploy diabetes-api \
--image gcr.io/your-project/diabetes-api \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--port 80 \
--memory 2Gi \
--cpu 2
Azure Container Instances
# Login to Azure
az login
# Create resource group
az group create --name diabetes-rg --location eastus
# Deploy container
az container create \
--resource-group diabetes-rg \
--name diabetes-api \
--image your-registry.azurecr.io/diabetes-api:latest \
--dns-name-label diabetes-api-unique \
--ports 80
API Security
API Key Authentication
from fastapi import Security, HTTPException, status
from fastapi.security import APIKeyHeader
import os
api_key_header = APIKeyHeader( name = "X-API-Key" )
def verify_api_key ( api_key : str = Security(api_key_header)):
if api_key != os.getenv( "API_KEY" ):
raise HTTPException(
status_code = status. HTTP_401_UNAUTHORIZED ,
detail = "Invalid API Key"
)
return api_key
@app.post ( "/predict" , dependencies = [Security(verify_api_key)])
def predict ( patient : Patient):
return pr.predict(patient)
Rate Limiting
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
limiter = Limiter( key_func = get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
@app.post ( "/predict" )
@limiter.limit ( "10/minute" )
def predict ( request : Request, patient : Patient):
return pr.predict(patient)
CORS Configuration
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins = [
"https://your-frontend.com" ,
"https://app.your-domain.com"
],
allow_credentials = True ,
allow_methods = [ "POST" , "GET" ],
allow_headers = [ "*" ],
)
Load Testing
Test API performance with locust:
# locustfile.py
from locust import HttpUser, task, between
class DiabetesAPIUser ( HttpUser ):
wait_time = between( 1 , 3 )
@task
def predict_diabetes ( self ):
self .client.post( "/predict" , json = {
"gender" : "Female" ,
"age" : 36 ,
"hypertension" : 0 ,
"heart_disease" : 0 ,
"smoking_history" : "current" ,
"bmi" : 32.27 ,
"HbA1c_level" : 6.2 ,
"blood_glucose_level" : 220
})
Run load test:
locust -f locustfile.py --host http://localhost
Integration Examples
React Frontend
import { useState } from 'react' ;
function DiabetesPrediction () {
const [ patient , setPatient ] = useState ({
gender: 'Female' ,
age: 36 ,
hypertension: 0 ,
heart_disease: 0 ,
smoking_history: 'never' ,
bmi: 25.0 ,
HbA1c_level: 5.5 ,
blood_glucose_level: 100
});
const [ result , setResult ] = useState ( null );
const handleSubmit = async ( e ) => {
e . preventDefault ();
const response = await fetch ( 'http://localhost/predict' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'X-API-Key' : 'your-api-key'
},
body: JSON . stringify ( patient )
});
const data = await response . json ();
setResult ( data . message );
};
return (
< form onSubmit = { handleSubmit } >
{ /* Form inputs */ }
< button type = "submit" > Predict </ button >
{ result && < div className = "result" > { result } </ div > }
</ form >
);
}
Python Client
import requests
class DiabetesAPIClient :
def __init__ ( self , base_url , api_key = None ):
self .base_url = base_url
self .headers = {}
if api_key:
self .headers[ 'X-API-Key' ] = api_key
def train_model ( self ):
response = requests.post(
f " { self .base_url } /train" ,
headers = self .headers
)
response.raise_for_status()
return response.json()
def predict ( self , patient_data ):
response = requests.post(
f " { self .base_url } /predict" ,
json = patient_data,
headers = self .headers
)
response.raise_for_status()
return response.json()
# Usage
client = DiabetesAPIClient( "http://localhost" , api_key = "your-key" )
patient = {
"gender" : "Female" ,
"age" : 36 ,
"hypertension" : 0 ,
"heart_disease" : 0 ,
"smoking_history" : "current" ,
"bmi" : 32.27 ,
"HbA1c_level" : 6.2 ,
"blood_glucose_level" : 220
}
result = client.predict(patient)
print (result) # {"message": "Tiene diabetes"}
Mobile App (Flutter)
import 'package:http/http.dart' as http;
import 'dart:convert' ;
class DiabetesAPI {
final String baseUrl;
final String ? apiKey;
DiabetesAPI ( this .baseUrl, { this .apiKey});
Future < Map < String , dynamic >> predict ( Map < String , dynamic > patient) async {
final response = await http. post (
Uri . parse ( ' $ baseUrl /predict' ),
headers : {
'Content-Type' : 'application/json' ,
if (apiKey != null ) 'X-API-Key' : apiKey ! ,
},
body : jsonEncode (patient),
);
if (response.statusCode == 200 ) {
return jsonDecode (response.body);
} else {
throw Exception ( 'Failed to get prediction' );
}
}
}
// Usage
final api = DiabetesAPI ( 'http://your-server.com' );
final result = await api. predict ({
'gender' : 'Female' ,
'age' : 36 ,
'hypertension' : 0 ,
'heart_disease' : 0 ,
'smoking_history' : 'current' ,
'bmi' : 32.27 ,
'HbA1c_level' : 6.2 ,
'blood_glucose_level' : 220 ,
});
print (result[ 'message' ]); // "Tiene diabetes"
Troubleshooting
Cause : API container not running or unhealthySolutions: # Check container status
docker ps | grep diabetes-api
# Check logs
docker logs diabetes-api
# Restart container
docker restart diabetes-api
Causes : Large model, CPU constraints, or cold startSolutions:
Allocate more resources:
docker run -d --cpus= "2" --memory= "4g" ...
Keep model in memory (already done)
Use model caching:
from functools import lru_cache
@lru_cache ( maxsize = 1 )
def load_model ():
with open ( "model.pkl" , "rb" ) as f:
return pickle.load(f)
Error : Access-Control-Allow-Origin errorSolution : Add CORS middleware (see Security section above)
Best Practices
Version Your API
@app.post ( "/v1/predict" )
def predict_v1 ( patient : Patient):
return pr.predict(patient)
@app.post ( "/v2/predict" )
def predict_v2 ( patient : PatientV2):
# New version with additional features
pass
Document with OpenAPI
FastAPI auto-generates docs at /docs Customize: app = FastAPI(
title = "Diabetes Prediction API" ,
description = "ML-powered diabetes risk assessment" ,
version = "1.0.0"
)
Validate Input Thoroughly
from pydantic import BaseModel, Field, validator
class Patient ( BaseModel ):
age: int = Field( ... , ge = 0 , le = 120 )
bmi: float = Field( ... , ge = 10 , le = 100 )
@validator ( 'gender' )
def validate_gender ( cls , v ):
if v not in [ 'Female' , 'Male' , 'Other' ]:
raise ValueError ( 'Invalid gender' )
return v
Implement Graceful Shutdown
import signal
import sys
def signal_handler ( sig , frame ):
logger.info( "Shutting down gracefully..." )
sys.exit( 0 )
signal.signal(signal. SIGINT , signal_handler)
signal.signal(signal. SIGTERM , signal_handler)
Next Steps
Phase 3 Guide Complete Phase 3 walkthrough
Docker Setup Docker configuration and best practices
CLI Usage For batch processing, explore CLI tools
Model Architecture Understand the underlying model