2.6 — Variáveis de Ambiente e Modos
Configure diferentes ambientes (dev, staging, produção) com variáveis seguras.
Objetivos da Aula
- Entender o sistema de variáveis de ambiente do Vite
- Criar arquivos
.envpara diferentes ambientes - Diferenciar variáveis públicas e privadas
- Usar modos customizados
Como Variáveis de Ambiente Funcionam
O Vite usa arquivos .env para definir variáveis de ambiente:
ARQUIVOS .env
.env Carregado em TODOS os casos .env.local Carregado em todos, ignorado pelo git .env.[mode] Carregado apenas no modo especificado .env.[mode].local Carregado no modo, ignorado pelo git
Ordem de prioridade (maior para menor):
- 1 .env.[mode].local
- 2 .env.[mode]
- 3 .env.local
- 4 .env
Modos Padrão
| Comando | Modo |
|---|---|
vite / vite dev | development |
vite build | production |
vite preview | production |
Criando Arquivos .env
.env — Base (todos os ambientes)
# .env
# Variáveis compartilhadas entre todos os ambientes
# PÚBLICAS (acessíveis no cliente)
VITE_APP_NAME=Dashboard Vite
VITE_APP_VERSION=1.0.0
# PRIVADAS (só no servidor/build)
DATABASE_URL=postgresql://localhost:5432/mydb
SECRET_KEY=minha-chave-secreta .env.development — Desenvolvimento
# .env.development
# Sobrescreve .env quando mode=development
VITE_API_URL=http://localhost:8080/api
VITE_DEBUG=true
# Banco de dev
DATABASE_URL=postgresql://localhost:5432/mydb_dev .env.production — Produção
# .env.production
# Sobrescreve .env quando mode=production
VITE_API_URL=https://api.meuprojeto.com
VITE_DEBUG=false
# Banco de produção (nunca commite senhas reais!)
DATABASE_URL=postgresql://prod-server:5432/mydb_prod .env.local — Local (não commitado)
# .env.local
# NUNCA vai para o git - para secrets locais
# Chaves pessoais de API
VITE_MAPS_API_KEY=sua-chave-pessoal-aqui
GITHUB_TOKEN=ghp_xxxxxxxxxxxx .gitignore
# .gitignore
.env.local
.env.*.local Acessando Variáveis
No Cliente (VITE_ prefix)
Importante: Apenas variáveis com prefixo VITE_ são expostas ao cliente!
Cuidado com VITE_
Nunca use o prefixo
VITE_ para segredos como senhas, tokens ou chaves privadas. Tudo com VITE_ vai parar no bundle do navegador e qualquer pessoa pode ver no DevTools.// Funciona - tem prefixo VITE_
console.log(import.meta.env.VITE_APP_NAME)
console.log(import.meta.env.VITE_API_URL)
// undefined - sem prefixo VITE_
// DATABASE_URL e SECRET_KEY nao sao expostas
console.log(import.meta.env.DATABASE_URL)
// retorna undefined
console.log(import.meta.env.SECRET_KEY)
// retorna undefined Variáveis Automáticas
// Variáveis sempre disponíveis
// 'development' ou 'production'
import.meta.env.MODE
// URL base (config.base)
import.meta.env.BASE_URL
// true se production
import.meta.env.PROD
// true se development
import.meta.env.DEV
// true se server-side rendering
import.meta.env.SSR Exemplo Prático
// src/config.ts
interface AppConfig {
appName: string
apiUrl: string
debug: boolean
isDev: boolean
isProd: boolean
mode: string
}
export const config: AppConfig = {
appName: import.meta.env.VITE_APP_NAME,
apiUrl: import.meta.env.VITE_API_URL,
debug: import.meta.env.VITE_DEBUG === 'true',
isDev: import.meta.env.DEV,
isProd: import.meta.env.PROD,
mode: import.meta.env.MODE
}
// Uso condicional
if (config.isDev) {
console.log('Modo desenvolvimento')
console.log('Config:', config)
}
// API URL dinâmica
async function fetchData(
endpoint: string
): Promise<unknown> {
const url = `${config.apiUrl}/${endpoint}`
return fetch(url).then(r => r.json())
} TypeScript e Variáveis de Ambiente
Tipagem das Variáveis
Crie um arquivo de tipos:
// src/vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_APP_NAME: string
readonly VITE_APP_VERSION: string
readonly VITE_API_URL: string
readonly VITE_DEBUG: string
readonly VITE_MAPS_API_KEY?: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
} Agora você tem autocomplete!
// TypeScript sabe que essas variáveis existem
const apiUrl = import.meta.env.VITE_API_URL
// string
const debug = import.meta.env.VITE_DEBUG
// string SvelteKit tem algo melhor
No SvelteKit, prefira usar
$env/static/public e $env/static/private em vez de import.meta.env. Esses módulos sao type-safe, validados em build time, e o SvelteKit garante que variaveis privadas nunca vazem para o cliente.Modos Customizados
Criando um Modo Staging
# .env.staging
VITE_APP_NAME=Dashboard Vite (Staging)
VITE_API_URL=https://staging-api.meuprojeto.com
VITE_DEBUG=true // package.json
{
"scripts": {
"dev": "vite",
"build": "vite build",
"build:staging": "vite build --mode staging",
"preview": "vite preview"
}
} Modo de Preview/QA
# .env.preview
VITE_APP_NAME=Dashboard Vite (Preview)
VITE_API_URL=https://preview-api.meuprojeto.com
VITE_FEATURE_FLAGS={"newUI":true,"betaFeatures":true} {
"scripts": {
"build:preview": "vite build --mode preview"
}
} Usando no vite.config.ts
Carregando Variáveis na Config
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
import type { UserConfig } from 'vite'
export default defineConfig(({ mode }) => {
// Carrega variáveis de ambiente
// process.cwd() = diretório atual
// '' = prefixo vazio (carrega TODAS)
const env = loadEnv(
mode,
process.cwd(),
''
)
console.log(`Modo: ${mode}`)
console.log(`API URL: ${env.VITE_API_URL}`)
return {
define: {
// Injeta variáveis customizadas
__APP_VERSION__: JSON.stringify(
env.VITE_APP_VERSION
)
},
server: {
proxy: {
'/api': {
target: env.VITE_API_URL,
changeOrigin: true
}
}
}
} satisfies UserConfig
}) Variáveis em Tempo de Build
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
define: {
// Disponíveis em todo o código
__BUILD_TIME__: JSON.stringify(
new Date().toISOString()
),
__COMMIT_HASH__: JSON.stringify(
process.env.COMMIT_SHA || 'local'
)
}
}) // No código
// declare para TypeScript reconhecer
declare const __BUILD_TIME__: string
declare const __COMMIT_HASH__: string
console.log(`Build: ${__BUILD_TIME__}`)
console.log(`Commit: ${__COMMIT_HASH__}`) Substituição de Variáveis em HTML
index.html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<title>%VITE_APP_NAME%</title>
<meta
name="version"
content="%VITE_APP_VERSION%"
>
</head>
<body>
<div id="app"></div>
<script
type="module"
src="/src/main.ts"
></script>
</body>
</html> O Vite substitui %VITE_*% automaticamente!
Segurança
Regras Importantes
SEGURANCA DE VARIAVEIS
VITE_ (publico) — Visivel no navegador, ok expor
- URLs de API publicas
- Nomes de app
- Feature flags publicos
- Chaves de API publicas (Google Maps, etc)
SEM PREFIXO (privado) — NUNCA exposto, seguro
- Senhas de banco
- Tokens de autenticacao
- Chaves secretas
- API keys privadas
NUNCA faca:
VITE_DATABASE_PASSWORD=...VITE_STRIPE_SECRET_KEY=...- Commitar .env.local
O que acontece se eu colocar uma senha com VITE_ prefix?
A senha vai ser incluida no bundle JavaScript final e qualquer pessoa que abrir o DevTools do navegador consegue ver. Isso vale para qualquer variavel com prefixo
VITE_ — ela e substituida em tempo de build como texto puro no codigo. Nunca coloque senhas, tokens secretos ou chaves privadas com esse prefixo.Validação de Variáveis
// src/env.ts
// Valida que todas as variáveis necessárias existem
const requiredEnvVars: string[] = [
'VITE_APP_NAME',
'VITE_API_URL'
]
for (const envVar of requiredEnvVars) {
if (!import.meta.env[envVar]) {
throw new Error(
`Variável de ambiente ${envVar} não definida!`
)
}
}
interface Env {
appName: string
apiUrl: string
debug: boolean
isDev: boolean
isProd: boolean
}
export const env: Env = {
appName: import.meta.env.VITE_APP_NAME,
apiUrl: import.meta.env.VITE_API_URL,
debug: import.meta.env.VITE_DEBUG === 'true',
isDev: import.meta.env.DEV,
isProd: import.meta.env.PROD
} Mini-Projeto: Configuração Multi-Ambiente
Vamos configurar nosso Dashboard para múltiplos ambientes:
Passo 1: Criar arquivos .env
# .env
VITE_APP_NAME=Dashboard Vite
VITE_APP_VERSION=1.0.0 # .env.development
VITE_API_URL=http://localhost:3001
VITE_DEBUG=true
VITE_ENVIRONMENT=development # .env.staging
VITE_API_URL=https://staging-api.example.com
VITE_DEBUG=true
VITE_ENVIRONMENT=staging # .env.production
VITE_API_URL=https://api.example.com
VITE_DEBUG=false
VITE_ENVIRONMENT=production Passo 2: Criar módulo de configuração
// src/config/env.ts
interface AppEnv {
app: {
name: string
version: string
}
api: {
url: string
}
flags: {
debug: boolean
}
runtime: {
mode: string
isDev: boolean
isProd: boolean
environment: string
}
}
// Validação
const required = [
'VITE_APP_NAME',
'VITE_API_URL'
]
for (const key of required) {
if (!import.meta.env[key]) {
console.error(
`Variável ${key} não definida!`
)
}
}
// Exporta configuração tipada
export const env: AppEnv = {
app: {
name: import.meta.env.VITE_APP_NAME,
version:
import.meta.env.VITE_APP_VERSION || '0.0.0'
},
api: {
url: import.meta.env.VITE_API_URL
},
flags: {
debug:
import.meta.env.VITE_DEBUG === 'true'
},
runtime: {
mode: import.meta.env.MODE,
isDev: import.meta.env.DEV,
isProd: import.meta.env.PROD,
environment:
import.meta.env.VITE_ENVIRONMENT
|| 'unknown'
}
}
// Log em desenvolvimento
if (env.flags.debug) {
console.log('Configuração carregada:', env)
} Passo 3: Componente de status do ambiente
<!-- src/components/EnvironmentBadge.svelte -->
<script lang="ts">
import { env } from '$lib/config/env'
const colors: Record<string, string> = {
// verde
development: '#22c55e',
// amarelo
staging: '#f59e0b',
// vermelho
production: '#ef4444'
}
const bgColor =
colors[env.runtime.environment] || '#666'
const label =
`${env.runtime.environment} • v${env.app.version}`
const visible =
!(env.runtime.isProd && !env.flags.debug)
</script>
{#if visible}
<div
class="environment-badge"
style="
position: fixed;
bottom: 1rem;
right: 1rem;
padding: 0.5rem 1rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
background: {bgColor};
color: white;
z-index: 9999;
"
>
{label}
</div>
{/if} Passo 4: Atualizar main.ts
<!-- src/App.svelte -->
<script lang="ts">
import { env } from '$lib/config/env'
import EnvironmentBadge
from './components/EnvironmentBadge.svelte'
// Atualiza título com nome do app
document.title =
`${env.app.name} - ${env.runtime.environment}`
</script>
<!-- conteúdo do app -->
<slot />
<EnvironmentBadge /> Passo 5: Scripts no package.json
{
"scripts": {
"dev": "vite",
"build": "vite build",
"build:staging": "vite build --mode staging",
"preview": "vite preview",
"preview:staging": "vite preview --mode staging"
}
} Desafio da Aula
Objetivo
Criar um sistema de feature flags baseado em variáveis de ambiente.
Instruções
- Crie uma variável
VITE_FEATURE_FLAGScom JSON de features - Crie um módulo que parseia e expõe as flags
- Use as flags para mostrar/esconder um card “Beta Features”
Exemplo de .env
VITE_FEATURE_FLAGS={"darkMode":true,"betaCard":true,"analytics":false} Spec de Verificação
- O módulo
featureFlagsexporta um objeto com as flags - Quando
betaCard: true, um card especial aparece no dashboard - Quando
betaCard: false, o card não aparece
Solução
Clique para ver a solução
// src/config/features.ts
const flagsJson =
import.meta.env.VITE_FEATURE_FLAGS || '{}'
interface FeatureFlags {
darkMode: boolean
betaCard: boolean
analytics: boolean
}
let flags: Partial<FeatureFlags> = {}
try {
flags = JSON.parse(flagsJson)
} catch (e) {
console.error(
'Erro ao parsear feature flags:',
e
)
}
export const featureFlags: FeatureFlags = {
darkMode: flags.darkMode ?? false,
betaCard: flags.betaCard ?? false,
analytics: flags.analytics ?? false
}
export function isEnabled(
flag: keyof FeatureFlags
): boolean {
return featureFlags[flag] === true
} <!-- src/components/BetaCard.svelte -->
<script lang="ts">
import { isEnabled }
from '$lib/config/features'
</script>
{#if isEnabled('betaCard')}
<div class="performance-card beta-card">
<span class="beta-badge">BETA</span>
<h3 class="card-title">
Funcionalidade Beta
</h3>
<p>
Esta é uma funcionalidade experimental!
</p>
</div>
{/if} <!-- No componente pai -->
<script lang="ts">
import BetaCard
from './components/BetaCard.svelte'
</script>
<!-- Na área do grid de cards -->
<BetaCard />Próxima aula: 2.7 — Build de Produção e Otimização