Gestire .env e Variabili d'Ambiente in Node.js

THEJORD Team1 min di lettura
nodejsenvconfigurazionesicurezza

Gestire .env e variabili d'ambiente in Node.js: dotenv, sicurezza e best practices. Guida completa per configurazioni sicure in sviluppo e produzione.

Gestire .env e Variabili d'Ambiente in Node.js

Cosa Sono le Variabili d'Ambiente

Le variabili d'ambiente sono coppie chiave-valore che configurano il comportamento delle applicazioni senza modificare il codice. Sono fondamentali per separare configurazione da codice, gestire secrets in modo sicuro e differenziare ambienti (sviluppo, staging, produzione). In Node.js, il pattern .env è diventato lo standard de facto.

Il File .env

Struttura Base

# .env - file di configurazione locale
# Commenti iniziano con #

# Database
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
DB_HOST=localhost
DB_PORT=5432
DB_USER=admin
DB_PASSWORD=secret123

# API Keys
API_KEY=sk_live_abcd1234567890
STRIPE_SECRET_KEY=sk_test_xyz
OPENAI_API_KEY=sk-abc123

# Configurazione app
NODE_ENV=development
PORT=3000
LOG_LEVEL=debug

# Feature flags
ENABLE_NEW_FEATURE=true
MAINTENANCE_MODE=false

# URLs
FRONTEND_URL=http://localhost:3000
API_URL=http://localhost:4000/api

Regole di Sintassi

# Stringhe semplici (senza quotes)
KEY=value

# Stringhe con spazi (richiedono quotes)
MESSAGE="Hello World"
GREETING='Welcome to the app'

# Multiline (con \n o quotes)
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIE..."

# Variabili con riferimento (alcuni parser supportano)
BASE_URL=https://api.example.com
FULL_URL=${BASE_URL}/v1

# Valori vuoti
EMPTY_VAR=
ALSO_EMPTY=""

Dotenv in Node.js

Setup Base

# Installazione
npm install dotenv

# Struttura progetto
project/
├── .env              # Valori locali (gitignore!)
├── .env.example      # Template per altri dev
├── .env.production   # Prod values (opzionale)
└── src/
    └── index.js
// src/index.js - Carica all'inizio dell'app
require('dotenv').config();
// oppure con ES modules
import 'dotenv/config';

// Accedi alle variabili
const port = process.env.PORT || 3000;
const dbUrl = process.env.DATABASE_URL;

console.log(`Server running on port ${port}`);

Configurazione Avanzata

// Specifica percorso file .env
require('dotenv').config({ path: '.env.local' });

// Carica più file in ordine
require('dotenv').config({ path: '.env' });
require('dotenv').config({ path: '.env.local', override: true });

// ES modules con import
import { config } from 'dotenv';
config({ path: '.env.production' });

// Accesso sicuro con fallback
const apiKey = process.env.API_KEY ?? 'default-key';
const port = parseInt(process.env.PORT, 10) || 3000;

Validazione Variabili

Con Zod

import { z } from 'zod';

const envSchema = z.object({
  NODE_ENV: z.enum(['development', 'production', 'test']),
  PORT: z.string().transform(Number).default('3000'),
  DATABASE_URL: z.string().url(),
  API_KEY: z.string().min(10),
  ENABLE_FEATURE: z.string().transform(v => v === 'true').default('false'),
});

// Valida e esporta
export const env = envSchema.parse(process.env);

// TypeScript inferisce i tipi automaticamente
// env.PORT è number
// env.ENABLE_FEATURE è boolean

Con envalid

import { cleanEnv, str, port, bool, url } from 'envalid';

export const env = cleanEnv(process.env, {
  NODE_ENV: str({ choices: ['development', 'production', 'test'] }),
  PORT: port({ default: 3000 }),
  DATABASE_URL: url(),
  API_KEY: str(),
  DEBUG: bool({ default: false }),
});

// Errore chiaro se manca una variabile richiesta
// "Missing environment variables: DATABASE_URL, API_KEY"

Ambienti Multipli

File per Ambiente

# Struttura file
.env                # Default/shared values
.env.local          # Override locali (gitignore)
.env.development    # Solo sviluppo
.env.production     # Solo produzione
.env.test           # Solo test

# Caricamento condizionale
const envFile = process.env.NODE_ENV === 'production'
  ? '.env.production'
  : '.env.development';

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

Next.js Built-in Support

# Next.js carica automaticamente in ordine:
.env.local          # Sempre ignorato da git
.env.[NODE_ENV]     # Per ambiente specifico
.env                # Default

# Prefisso NEXT_PUBLIC_ per esporre al client
NEXT_PUBLIC_API_URL=https://api.example.com
DATABASE_URL=postgresql://...  # Solo server

// Nel codice client
const apiUrl = process.env.NEXT_PUBLIC_API_URL;

Sicurezza

Cosa NON Fare

# MAI committare secrets nel repo!
# .gitignore DEVE contenere:
.env
.env.local
.env*.local
*.pem
*.key

# MAI hardcodare secrets
const apiKey = 'sk_live_abc123';  // MALE!

# MAI loggare secrets
console.log(process.env);  // Espone tutto!
console.log('API Key:', process.env.API_KEY);  // MALE!

Best Practices

# Crea .env.example come template
# .env.example (committato nel repo)
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
API_KEY=your-api-key-here
PORT=3000

# Usa secrets manager in produzione
# - AWS Secrets Manager
# - HashiCorp Vault
# - Doppler
# - 1Password Secrets Automation

# Rotazione periodica delle chiavi
# - Cambia API keys ogni 90 giorni
# - Usa short-lived tokens dove possibile

CI/CD

GitHub Actions

# .github/workflows/deploy.yml
name: Deploy

on: push

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build with env
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          API_KEY: ${{ secrets.API_KEY }}
          NODE_ENV: production
        run: |
          npm ci
          npm run build

# Aggiungi secrets in:
# Repository → Settings → Secrets and variables → Actions

Vercel/Netlify

# Aggiungi variabili nel dashboard:
# Project → Settings → Environment Variables

# Variabili per ambiente specifico
# Production: API_URL=https://api.prod.com
# Preview:    API_URL=https://api.staging.com
# Development: API_URL=http://localhost:4000

Docker

# docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    environment:
      - NODE_ENV=production
      - PORT=3000
    env_file:
      - .env.production

# Dockerfile
FROM node:20-alpine
ENV NODE_ENV=production
# ...

# Runtime override
docker run -e API_KEY=xyz -e DEBUG=true myapp

Pattern Comuni

Config Module

// config/index.js
import 'dotenv/config';
import { z } from 'zod';

const envSchema = z.object({
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  PORT: z.coerce.number().default(3000),
  DATABASE_URL: z.string(),
  REDIS_URL: z.string().optional(),
  LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
});

const parsed = envSchema.safeParse(process.env);

if (!parsed.success) {
  console.error('Invalid environment variables:', parsed.error.flatten());
  process.exit(1);
}

export const config = {
  env: parsed.data.NODE_ENV,
  port: parsed.data.PORT,
  db: {
    url: parsed.data.DATABASE_URL,
  },
  redis: parsed.data.REDIS_URL,
  logging: {
    level: parsed.data.LOG_LEVEL,
  },
  isDev: parsed.data.NODE_ENV === 'development',
  isProd: parsed.data.NODE_ENV === 'production',
};

// Uso
import { config } from './config';
app.listen(config.port);

Debugging

Il debugging delle variabili d'ambiente può essere complicato. Se una variabile non viene caricata, verifica prima che il file .env sia nella directory corretta. Usa process.cwd() per controllare la working directory. Assicurati che dotenv.config() sia chiamato prima di accedere a process.env. Per debugging, puoi loggare Object.keys(process.env) per vedere tutte le variabili caricate. Ricorda che le variabili definite nel sistema hanno precedenza su quelle del file .env.

Errori Comuni

Gli errori più frequenti includono: spazi intorno al segno uguale che causano valori errati, virgolette non chiuse nelle stringhe multilinea, file .env non salvato in UTF-8, path relativo sbagliato a dotenv.config. Per variabili booleane, ricorda che process.env restituisce sempre stringhe quindi ENABLE_FEATURE=true sarà la stringa true non il booleano. Usa validazione con Zod o envalid per convertire automaticamente i tipi e catturare errori di configurazione all'avvio dell'applicazione invece che a runtime.

Strumenti Correlati

Per lavorare con configurazioni:

Conclusione

Le variabili d'ambiente sono fondamentali per:

  • Separare configurazione da codice
  • Gestire secrets in modo sicuro
  • Supportare ambienti multipli
  • Facilitare deployment CI/CD

Ricorda: mai committare .env con secrets reali! Usa git hooks con husky per prevenire commit accidentali di file .env. Configura pattern matching nel .gitignore per escludere tutte le varianti del file env.

Per altri strumenti utili, esplora i nostri tool online gratuiti. Per approfondimenti, consulta dotenv documentation.