Variáveis de ambiente com dotenv

1. Introdução às variáveis de ambiente

Variáveis de ambiente são pares chave-valor armazenados no sistema operacional ou em arquivos de configuração, que influenciam o comportamento de aplicações em tempo de execução. Em projetos JavaScript, elas são essenciais para separar configurações sensíveis do código-fonte.

O principal problema que resolvem é a segurança: senhas de banco de dados, chaves de API, tokens JWT e outras credenciais não devem jamais ser escritas diretamente no código. Se um repositório for comprometido ou tornado público, esses dados ficam expostos. Além disso, variáveis de ambiente permitem que diferentes ambientes (desenvolvimento, teste, produção) usem configurações distintas sem alterar o código.

O arquivo .env tornou-se o padrão de facto para gerenciar essas variáveis em projetos Node.js, e a biblioteca dotenv é a ferramenta mais utilizada para carregá-las automaticamente.

2. Instalação e configuração básica do dotenv

A instalação é simples via npm:

npm install dotenv

No arquivo principal da aplicação (geralmente index.js, app.js ou server.js), adicione o carregamento automático:

require('dotenv').config();

// Restante da aplicação
const express = require('express');
const app = express();

O arquivo .env deve estar na raiz do projeto com a seguinte sintaxe:

PORT=3000
DB_HOST=localhost
DB_USER=admin
DB_PASS=senha_segura
JWT_SECRET=minha_chave_secreta

Importante: não use aspas nos valores. Se precisar de espaços, o valor deve ser mantido sem aspas — o dotenv lida com isso automaticamente.

3. Acessando variáveis de ambiente no Node.js

Após configurar o dotenv, as variáveis ficam disponíveis em process.env:

const port = process.env.PORT;
const dbHost = process.env.DB_HOST;

console.log(`Servidor rodando na porta ${port}`);

Para valores padrão (fallback), use o operador ||:

const port = process.env.PORT || 3000;

Como tudo é string, é necessário converter tipos:

// Número
const port = Number(process.env.PORT) || 3000;

// Booleano
const isDev = process.env.NODE_ENV === 'development';

// JSON
const config = JSON.parse(process.env.APP_CONFIG || '{}');

4. Boas práticas com dotenv em projetos Node.js + Express

Em aplicações Express, é comum organizar variáveis por ambiente:

// .env.development
PORT=3000
DB_URL=mongodb://localhost:27017/dev_db
JWT_SECRET=dev_secret_key

// .env.production
PORT=8080
DB_URL=mongodb://prod-server:27017/prod_db
JWT_SECRET=prod_secret_key_super_segura

Carregue o arquivo correto baseado no ambiente:

const path = require('path');
const envFile = process.env.NODE_ENV === 'production' 
  ? '.env.production' 
  : '.env.development';

require('dotenv').config({ path: path.resolve(__dirname, '..', envFile) });

Exemplo prático completo com Express + PostgreSQL + JWT:

require('dotenv').config();

const express = require('express');
const { Pool } = require('pg');
const jwt = require('jsonwebtoken');

const app = express();
const port = process.env.PORT || 3000;

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false
});

app.post('/login', async (req, res) => {
  const token = jwt.sign(
    { userId: 123 },
    process.env.JWT_SECRET,
    { expiresIn: process.env.JWT_EXPIRES_IN || '1h' }
  );
  res.json({ token });
});

app.listen(port, () => {
  console.log(`Servidor rodando na porta ${port}`);
});

5. Integração com React (Create React App e Vite)

No frontend React, as variáveis de ambiente funcionam de forma diferente. Create React App exige o prefixo REACT_APP_:

// .env (na raiz do projeto CRA)
REACT_APP_API_URL=https://api.exemplo.com
REACT_APP_GOOGLE_CLIENT_ID=123456789

No código React:

const apiUrl = process.env.REACT_APP_API_URL;
const googleClientId = process.env.REACT_APP_GOOGLE_CLIENT_ID;

Com Vite, o prefixo é VITE_:

// .env
VITE_API_URL=https://api.exemplo.com

// Código React
const apiUrl = import.meta.env.VITE_API_URL;

Diferença crucial: variáveis de ambiente do lado do cliente (React) são injetadas em tempo de build e ficam visíveis no bundle final. Já no Node.js (backend), são lidas em tempo de execução e permanecem no servidor. Nunca coloque chaves secretas de backend em variáveis REACT_APP_ ou VITE_.

Para compartilhar variáveis comuns (como URL da API), crie um arquivo .env na raiz do monorepo e referencie-o em ambos os projetos.

6. Segurança e versionamento

O passo mais importante: adicione .env ao .gitignore:

# .gitignore
.env
.env.*.local

Crie um arquivo .env.example como template para outros desenvolvedores:

# .env.example
PORT=3000
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
JWT_SECRET=your_secret_key_here
NODE_ENV=development

Valide variáveis obrigatórias na inicialização:

const requiredVars = ['DATABASE_URL', 'JWT_SECRET', 'PORT'];
requiredVars.forEach(varName => {
  if (!process.env[varName]) {
    throw new Error(`Variável obrigatória ${varName} não definida`);
  }
});

7. Depuração e troubleshooting

Para verificar se o dotenv carregou corretamente:

const result = require('dotenv').config();
if (result.error) {
  console.error('Erro ao carregar .env:', result.error);
} else {
  console.log('.env carregado com sucesso');
}

Problemas comuns:
- Caminho incorreto: use path.resolve(__dirname, '.env') para garantir o caminho absoluto
- Sobrescrita: variáveis já existentes no sistema não são sobrescritas por padrão. Use override: true se necessário
- Encoding: arquivos .env devem ser salvos em UTF-8 sem BOM

Para interpolação de variáveis (ex: DB_URL=mongodb://${DB_USER}:${DB_PASS}@localhost), instale dotenv-expand:

npm install dotenv-expand

const dotenv = require('dotenv');
const dotenvExpand = require('dotenv-expand');
dotenvExpand.expand(dotenv.config());

8. Alternativas e considerações finais

Embora dotenv seja excelente para desenvolvimento, em produção é comum usar variáveis de ambiente nativas do sistema operacional (Docker, Heroku, AWS Lambda). O dotenv pode ser usado como fallback.

Para validação tipada, considere envalid:

npm install envalid

const { cleanEnv, str, num } = require('envalid');
const env = cleanEnv(process.env, {
  PORT: num({ default: 3000 }),
  DATABASE_URL: str(),
  NODE_ENV: str({ choices: ['development', 'production'] })
});
// env.PORT é number, env.DATABASE_URL é string obrigatória

Resumo das melhores práticas:
1. Sempre use .env para desenvolvimento local
2. Nunca versionar arquivos .env reais
3. Forneça .env.example com valores fictícios
4. Valide variáveis obrigatórias na inicialização
5. Use prefixos específicos (REACT_APP_, VITE_) no frontend
6. Separe configurações por ambiente
7. Prefira variáveis nativas em produção

Com essas práticas, suas aplicações JavaScript + Node.js + React ficarão mais seguras, portáteis e fáceis de configurar em qualquer ambiente.

Referências