Configuração de aplicações TypeScript: do simples ao complexo
Qualquer aplicação moderna requer uma configuração flexível e segura. Sejam chaves de API, parâmetros de conexão com banco de dados, portas do servidor ou flags de funcionalidades — a configuração deve ser separada do código, fácil de ler e validada na inicialização. No mundo do TypeScript, esta tarefa tem suas particularidades: a tipagem da configuração oferece autocompletar e previne erros em tempo de execução. Neste artigo, abordaremos as melhores práticas, bibliotecas populares e ferramentas para gerenciar a configuração de aplicações TypeScript.
Por que não usar process.env diretamente?
À primeira vista, ler variáveis de ambiente através de process.env parece uma solução simples. No entanto, esta abordagem tem desvantagens significativas:
- Falta de tipagem: todos os valores são strings, mesmo que logicamente sejam números ou booleanos.
- Risco de erros de digitação: é fácil escrever
process.env.DB_HOTSem vez deDB_HOSTe obterundefined. - Falta de validação: se uma variável obrigatória não for definida, a aplicação falhará no momento mais inesperado, e não na inicialização.
Uma configuração adequada resolve todos esses problemas.
Biblioteca dotenv e seus wrappers para TypeScript
O padrão de facto para carregar arquivos .env é a biblioteca dotenv. Para TypeScript, é conveniente usar dotenv-flow ou dotenv-expand, que suportam múltiplos ambientes (development, staging, production).
// Instalação// npm install dotenv dotenv-expand
// config/env.tsimport dotenv from 'dotenv';import dotenvExpand from 'dotenv-expand';
const myEnv = dotenv.config({ path: `.env.${process.env.NODE_ENV || 'development'}` });dotenvExpand.expand(myEnv);Após chamar esta função, todas as variáveis do arquivo .env estarão disponíveis através de process.env, mas ainda não temos tipos.
Validação de esquema com Zod
Zod é uma biblioteca poderosa para validação declarativa de esquemas com suporte de primeira classe para TypeScript. É ideal para configuração:
// config/schema.tsimport { z } from 'zod';
const envSchema = z.object({ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'), PORT: z.coerce.number().positive().default(3000), DATABASE_URL: z.string().url(), JWT_SECRET: z.string().min(32), REDIS_HOST: z.string().default('localhost'), REDIS_PORT: z.coerce.number().default(6379), FEATURE_NEW_UI: z .string() .transform((val) => val === 'true') .default('false'),});
export type EnvConfig = z.infer<typeof envSchema>;Agora podemos analisar e validar o ambiente com segurança na inicialização:
// config/index.tsimport { envSchema, EnvConfig } from './schema';
let config: EnvConfig;
export function loadConfig(): EnvConfig { if (config) return config; // cache
const result = envSchema.safeParse(process.env); if (!result.success) { console.error('❌ Variáveis de ambiente inválidas:', result.error.flatten().fieldErrors); process.exit(1); } config = result.data; return config;}
export function getConfig(): EnvConfig { if (!config) throw new Error('Config não carregada. Chame loadConfig() primeiro.'); return config;}Biblioteca Conf para gerenciamento de configuração da aplicação
Se sua aplicação requer não apenas leitura, mas também escrita de configuração (por exemplo, configurações do usuário, cache), confira conf de Sindre Sorhus. Ela armazena dados em um arquivo JSON e é totalmente tipada.
// Instalação// npm install conf
import Conf from 'conf';
interface AppConfig { lastOp