Pydantic Settings: configuración segura

Publicado el: 23/07/2026
Tempo de leitura: 5 minutos
Configuración segura de aplicaciones Python con Pydantic Settings

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-settings

El 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_local

Al 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 == 8000

Con 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.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desenvolvedor programando em Python com Ruff para lint e formatação
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Ruff en Python: lint y formato de código paso a paso

    Aprende Ruff en Python para lint, formato, correcciones automáticas, pyproject.toml, VS Code y CI con una configuración práctica.

    Ler mais

    Tempo de leitura: 10 minutos
    22/07/2026
    Dicas para melhorar performance de scripts Python lentos
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Por qué Python puede ser lento y cómo mejorar su rendimiento

    Descubre por qué Python puede ser lento y mejora su rendimiento con cProfile, algoritmos, sets, generadores, NumPy, caché y concurrencia.

    Ler mais

    Tempo de leitura: 5 minutos
    12/07/2026
    Leitura segura de senhas no terminal usando Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Cómo leer contraseñas de forma segura en el terminal con Python

    Lee contraseñas de forma segura con getpass, valida entradas, evita logs y texto plano y almacena credenciales con hashing adecuado.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Exemplo de testes unitários em Python com código de unittest para validação automatizada
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Tests unitarios en Python: unittest, pytest y mocks

    Aprende tests unitarios en Python con unittest, pytest, fixtures, parametrización, mocks, cobertura y buenas prácticas para evitar regresiones.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Proteção de API Flask usando autenticação JWT em Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Cómo proteger una API Flask con JWT

    Protege una API Flask con JWT, access y refresh tokens, bcrypt, roles, revocación, variables de entorno, HTTPS y pruebas de

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Enums en Python: evita valores mágicos y errores

    Aprende enums en Python con Enum, IntEnum, StrEnum, auto, unique, Flag, validación, JSON, bases de datos y buenas prácticas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026