Configurações de aplicação parecem simples enquanto o projeto possui apenas duas ou três variáveis. Porém, quando entram banco de dados, APIs externas, ambientes de desenvolvimento, testes e produção, o código pode virar uma mistura perigosa de valores fixos, chamadas a os.getenv() e verificações espalhadas. O Pydantic Settings resolve esse problema ao reunir as configurações em classes tipadas, validar valores automaticamente e carregar dados de variáveis de ambiente ou arquivos .env.
Neste guia, você aprenderá a criar uma configuração centralizada, declarar tipos, trabalhar com valores obrigatórios, converter listas e URLs, separar ambientes, proteger segredos e integrar tudo a uma aplicação FastAPI. A proposta é transformar configurações frágeis em um componente previsível e fácil de testar.
Por que centralizar configurações?
Um projeto pequeno pode funcionar com chamadas isoladas a os.getenv(). O problema aparece quando cada módulo busca uma variável diferente, aplica um valor padrão próprio ou converte tipos de maneiras inconsistentes. Uma porta pode chegar como texto, um modo de depuração pode ser interpretado incorretamente e uma senha ausente pode causar erro apenas depois que a aplicação já iniciou.
Centralizar as configurações permite validar tudo logo na inicialização. Se um valor obrigatório não existir ou estiver no formato errado, a aplicação falha cedo com uma mensagem clara. Isso reduz erros silenciosos e torna a implantação mais segura. Para entender a base, veja também como ler variáveis de ambiente em Python.
Instalando o Pydantic Settings
Crie um ambiente virtual e instale o pacote:
python -m venv .venv
# Windows: .venv\Scripts\activate
# Linux/macOS: source .venv/bin/activate
pip install pydantic-settingsO pacote utiliza o Pydantic para validar os dados e fornece a classe BaseSettings. Se você ainda não organiza dependências por projeto, vale revisar o guia sobre ambientes virtuais com venv.
Criando a primeira classe de configurações
Crie um arquivo config.py:
from pydantic import SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
app_name: str = "Minha 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()A classe declara os nomes e os tipos esperados. database_url e api_key são obrigatórios porque não possuem valores padrão. O campo api_key usa SecretStr, que evita mostrar o segredo completo em logs e representações do objeto.
Crie um arquivo .env:
APP_NAME=Academify API
DEBUG=true
PORT=8080
DATABASE_URL=sqlite:///app.db
API_KEY=segredo_localAo instanciar Settings, o Pydantic converte true para booleano e 8080 para inteiro. Esse comportamento reduz conversões manuais e documenta o formato esperado diretamente no código.
Como os nomes das variáveis são resolvidos
Por padrão, os campos podem ser preenchidos por variáveis de ambiente com o mesmo nome, sem diferenciar maiúsculas e minúsculas em muitos ambientes. Para adotar um prefixo e evitar conflitos, configure env_prefix:
model_config = SettingsConfigDict(
env_file=".env",
env_prefix="APP_",
extra="ignore",
)Nesse caso, o campo database_url será lido de APP_DATABASE_URL. Prefixos são úteis quando vários serviços compartilham o mesmo servidor ou arquivo de implantação.
Validando valores
Além de converter tipos, você pode impor limites:
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)Agora uma porta negativa ou um número exagerado de processos será rejeitado imediatamente. Esse estilo se beneficia das anotações explicadas no artigo sobre type hints em Python.
Listas e estruturas mais complexas
Valores complexos podem ser fornecidos em JSON:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
allowed_hosts: list[str] = ["localhost"]
feature_flags: dict[str, bool] = {}
No arquivo .env:
ALLOWED_HOSTS=["academify.com.br", "api.academify.com.br"]
FEATURE_FLAGS={"novo_login": true, "cache": false}Isso é mais seguro do que dividir textos manualmente por vírgulas, especialmente quando os valores podem conter espaços ou caracteres especiais.
Configurações aninhadas
Projetos maiores ficam mais claros quando as configurações são agrupadas:
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="__"
)Com o delimitador duplo, você pode usar DATABASE__HOST e DATABASE__PORT. Esse padrão mantém o objeto organizado sem perder a compatibilidade com variáveis de ambiente.
Integração com FastAPI
Em uma API, evite criar uma nova instância a cada requisição. Use cache:
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,
}A função é executada apenas uma vez por processo. O padrão combina bem com o tutorial sobre como criar APIs com FastAPI e com o uso de lru_cache em Python.
Separando desenvolvimento, teste e produção
Evite manter um único arquivo com valores para todos os ambientes. Uma estratégia simples é escolher o arquivo por uma variável externa:
import os
from pydantic_settings import SettingsConfigDict
ENV = os.getenv("APP_ENV", "development")
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=f".env.{ENV}",
extra="ignore",
)Você pode ter .env.development e .env.test, mas segredos de produção devem ser fornecidos pelo serviço de hospedagem, Docker, Kubernetes ou cofre de segredos. Nunca envie arquivos com credenciais reais ao Git.
Testando configurações
Como a configuração é uma classe, os testes podem substituir valores diretamente:
def test_settings():
config = Settings(
database_url="sqlite:///:memory:",
api_key="teste",
debug=True,
)
assert config.debug is True
assert config.port == 8000Também é possível usar o recurso monkeypatch do pytest para definir variáveis temporárias. Isso evita depender do computador do desenvolvedor e deixa os testes reproduzíveis.
Erros comuns
- Instanciar configurações em muitos módulos: centralize a criação ou use cache.
- Guardar segredos no repositório: mantenha apenas um arquivo
.env.example. - Usar texto para tudo: declare
bool,int, URLs, listas e tipos específicos. - Ignorar falhas de validação: deixe a aplicação falhar cedo quando a configuração estiver inválida.
- Registrar o objeto completo: mesmo com
SecretStr, evite logs desnecessários. - Confundir prioridade: variáveis reais de ambiente normalmente devem substituir o arquivo local.
Boas práticas para produção
Mantenha nomes consistentes, documente cada variável em .env.example, aplique o princípio do menor privilégio e rotacione segredos periodicamente. Valide URLs e limites, não apenas a presença do valor. Em contêineres, injete as variáveis no momento da execução. Em pipelines, use o gerenciador de segredos da plataforma e restrinja quem pode visualizar os valores.
A documentação oficial do Pydantic Settings detalha fontes, aliases, configurações aninhadas e personalização. Para entender o tratamento de segredos, consulte também a página de SecretStr.
Conclusão
O Pydantic Settings transforma variáveis de ambiente em uma interface tipada e validada. Em vez de espalhar conversões e valores padrão pelo projeto, você define uma única classe que documenta tudo o que a aplicação precisa. O resultado é um código mais previsível, uma inicialização mais segura e testes mais simples.
Comece com poucos campos, adicione validações relevantes e separe ambientes conforme o projeto crescer. O objetivo não é criar uma configuração complicada, mas garantir que erros de infraestrutura sejam detectados antes de afetarem usuários ou dados.







