La configuración de una aplicación parece sencilla cuando el proyecto solo necesita dos o tres valores. Sin embargo, al añadir bases de datos, APIs externas, entornos de desarrollo y producción, procesos en segundo plano, banderas de funciones y pruebas, la configuración puede convertirse en una colección frágil de textos fijos y llamadas dispersas a os.getenv(). Pydantic Settings ofrece un enfoque más claro: declarar los ajustes como campos tipados, cargar valores desde variables de entorno o archivos .env y validarlos al iniciar la aplicación.
En esta guía aprenderás a crear una clase central de configuración, exigir valores importantes, convertir booleanos y números, proteger secretos, organizar ajustes anidados, separar entornos, probar la configuración e integrarla con FastAPI. El objetivo no es complicar el proyecto, sino hacer que la configuración sea predecible, documentada y segura.
Por qué conviene centralizar la configuración
Las lecturas dispersas de variables de entorno generan inconsistencias. Un módulo puede aceptar un valor vacío, otro puede aplicar un valor predeterminado y un tercero puede fallar mucho más tarde. Los puertos llegan como cadenas, los booleanos pueden interpretarse mal y una URL inválida puede pasar desapercibida hasta que una petición falle.
Un objeto central valida toda la configuración al iniciar. Si falta un valor obligatorio o su formato es incorrecto, el programa falla pronto y muestra un error útil. Esto es mucho mejor que iniciar con éxito y romperse cuando ya existen usuarios. Para repasar la base, consulta nuestra guía sobre variables de entorno en Python.
Instalar Pydantic Settings
Crea un entorno virtual e instala el paquete:
python -m venv .venv
# Windows: .venv\Scripts\activate
# Linux/macOS: source .venv/bin/activate
pip install pydantic-settingsEl paquete utiliza Pydantic y proporciona la clase BaseSettings. Mantener las dependencias en un entorno propio evita conflictos entre proyectos. Si lo necesitas, revisa el tutorial sobre entornos virtuales con venv.
Crear la primera clase de ajustes
Crea el archivo config.py:
from pydantic import SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
app_name: str = "Mi API"
debug: bool = False
port: int = 8000
database_url: str
api_key: SecretStr
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
settings = Settings()La clase documenta cada valor esperado y su tipo. database_url y api_key son obligatorios porque no tienen valor predeterminado. SecretStr evita que el secreto completo aparezca en representaciones normales del objeto y reduce filtraciones accidentales en registros.
Crea un archivo local .env:
APP_NAME=Academify API
DEBUG=true
PORT=8080
DATABASE_URL=sqlite:///app.db
API_KEY=secreto_localAl crear Settings(), Pydantic convierte true en booleano y 8080 en entero. Si la conversión no es posible, la validación falla inmediatamente.
Usar prefijos para evitar conflictos
Un servidor puede alojar varios servicios. Un prefijo deja claro a qué aplicación pertenece cada variable:
model_config = SettingsConfigDict(
env_file=".env",
env_prefix="APP_",
extra="ignore",
)El campo database_url se leerá ahora desde APP_DATABASE_URL. Los prefijos también facilitan la administración en plataformas de despliegue con muchas variables.
Validar límites y formatos
El tipo es solo la primera capa. Usa restricciones para rechazar valores que sean del tipo correcto pero no tengan sentido operativo:
from pydantic import Field
class Settings(BaseSettings):
port: int = Field(default=8000, ge=1, le=65535)
workers: int = Field(default=1, ge=1, le=32)
timeout_seconds: float = Field(default=10.0, gt=0)Un tiempo de espera negativo o un puerto superior a 65535 será rechazado antes de iniciar el servidor. Este enfoque se relaciona con los conceptos explicados en type hints en Python.
Listas, diccionarios y valores JSON
Los ajustes también pueden contener estructuras:
class Settings(BaseSettings):
allowed_hosts: list[str] = ["localhost"]
feature_flags: dict[str, bool] = {}Proporciona los valores complejos como JSON:
ALLOWED_HOSTS=["academify.com.br", "api.academify.com.br"]
FEATURE_FLAGS={"nuevo_login": true, "cache": false}JSON evita divisiones ambiguas por comas y conserva los tipos dentro de las colecciones.
Configuraciones anidadas
Cuando el proyecto crece, agrupa valores relacionados:
from pydantic import BaseModel
from pydantic_settings import BaseSettings, SettingsConfigDict
class DatabaseSettings(BaseModel):
host: str = "localhost"
port: int = 5432
name: str = "app"
class Settings(BaseSettings):
database: DatabaseSettings = DatabaseSettings()
model_config = SettingsConfigDict(
env_nested_delimiter="__"
)Ahora puedes definir DATABASE__HOST y DATABASE__PORT. Los modelos anidados mantienen limpia la interfaz en Python sin perder compatibilidad con plataformas de despliegue.
Integración con FastAPI
Una API no debería construir la configuración en cada petición. Guarda el objeto en caché e inyéctalo como dependencia:
from functools import lru_cache
from fastapi import Depends, FastAPI
app = FastAPI()
@lru_cache
def get_settings() -> Settings:
return Settings()
@app.get("/info")
def info(config: Settings = Depends(get_settings)):
return {
"app_name": config.app_name,
"debug": config.debug,
}La caché crea una sola instancia por proceso. Este patrón combina bien con nuestra guía para crear APIs modernas con FastAPI.
Separar desarrollo, pruebas y producción
No mezcles todos los entornos en un solo archivo. Una estrategia local sencilla elige el archivo mediante una variable externa:
import os
from pydantic_settings import BaseSettings, SettingsConfigDict
ENV = os.getenv("APP_ENV", "development")
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=f".env.{ENV}",
extra="ignore",
)Puedes mantener .env.development y .env.test en local. Los secretos de producción deben proceder de la plataforma de alojamiento, Docker, Kubernetes o un gestor de secretos. Nunca subas credenciales reales al repositorio. El mismo principio se aplica al ejecutar Python con Docker.
Probar la configuración
Como los ajustes forman una clase, puedes pasar valores directamente en las pruebas:
def test_settings():
config = Settings(
database_url="sqlite:///:memory:",
api_key="prueba",
debug=True,
)
assert config.debug is True
assert config.port == 8000Con pytest, monkeypatch permite crear variables temporales. Así las pruebas no dependen del equipo del desarrollador y producen resultados consistentes.
Errores frecuentes
- Crear ajustes en cada módulo: centraliza la creación o usa caché.
- Guardar secretos en Git: publica solamente un archivo
.env.example. - Usar texto para todo: declara booleanos, enteros, listas, URLs y campos restringidos.
- Ignorar errores de validación: es mejor fallar al iniciar que durante una petición.
- Registrar toda la configuración: evita salidas innecesarias incluso con
SecretStr. - Confundir prioridades: las variables reales del entorno deberían reemplazar el archivo local.
Lista de comprobación para producción
Usa nombres consistentes, documenta cada variable, rota credenciales, limita permisos y valida formatos y rangos. Proporciona secretos únicamente en tiempo de ejecución. En integración continua, utiliza el almacenamiento protegido de la plataforma y evita variables visibles en registros.
La documentación oficial de Pydantic Settings explica fuentes, alias, modelos anidados y personalización. La referencia de SecretStr detalla cómo se representan y serializan los secretos.
Conclusión
Pydantic Settings convierte las variables de entorno en una interfaz tipada y validada. En lugar de distribuir conversiones y valores predeterminados por todo el proyecto, defines un modelo que documenta lo que la aplicación necesita. Los errores de despliegue se detectan antes, las pruebas son más sencillas y los valores sensibles se manejan con mayor cuidado.
Empieza con una clase pequeña, añade restricciones que reflejen límites reales y separa los entornos a medida que el proyecto crezca. Una buena configuración no es solo una cuestión de organización: también mejora la fiabilidad y la seguridad.







