Publicado em

- 5 minutos de leitura

Angular: Variáveis Ambiente com Define Option

img of Angular: Variáveis Ambiente com Define Option

Se preferir, assista o vídeo diretamente no YouTube:

Gerenciar configurações em diferentes ambientes (desenvolvimento, homologação e produção) é um desafio comum no desenvolvimento frontend.

Tradicionalmente, o Angular utiliza os arquivos environment.ts, mas com a chegada do novo Application Builder (baseado em esbuild e Vite), temos acesso a uma ferramenta poderosa chamada Define Option.

Essa funcionalidade permite definir constantes globais que são substituídas literalmente durante o processo de build.

Isso não apenas simplifica a injeção de valores, mas também permite otimizações de performance, como o tree-shaking (remoção de código morto), garantindo que trechos de código desativados sequer cheguem ao bundle final do usuário.

O que é a Define Option?

A Define Option é um mecanismo de “localizar e substituir” que ocorre em tempo de compilação (build-time).

Ela instrui o compilador a procurar por identificadores globais específicos no seu código TypeScript e substituí-los por valores constantes definidos na configuração do projeto.

Diferente de injetar um objeto de configuração em tempo de execução, essa substituição acontece antes do código ser minificado.

Se você definir uma constante como false, ferramentas de build inteligentes podem detectar blocos de código condicionados a essa variável e removê-los completamente do arquivo final, reduzindo o tamanho da aplicação.

Quando usar essa estratégia?

Você deve considerar o uso da Define Option quando precisar de valores que são fixos para um determinado ambiente e não mudarão enquanto a aplicação estiver rodando no navegador.

É a escolha ideal para Feature Flags (ativar ou desativar funcionalidades experimentais), onde você quer garantir que o código de uma funcionalidade desativada não seja enviado para o cliente.

Também é extremamente útil para injetar chaves de API públicas, URLs de backend ou versões do sistema diretamente no código, sem precisar importar arquivos de ambiente complexos em todos os serviços.

Define Option vs Arquivos de Environment: Qual escolher?

Com a existência dessas duas opções, pode surgir a dúvida de qual utilizar. A regra prática é baseada na complexidade do dado que você precisa configurar.

Use a Define Option quando tiver valores primitivos simples (strings, booleans ou números). Ela é superior para:

  • Feature Flags: Permite remover código morto (tree-shaking) de forma mais eficiente que os environments.
  • Segredos de Build: É mais fácil injetar valores simples via linha de comando no CI/CD sem precisar criar arquivos físicos.
  • Metadados: Versão do app, data de build ou URL base da API.

Continue usando Arquivos de Environment (environment.ts) quando tiver objetos complexos. Eles ainda são úteis para:

  • Configurações Agrupadas: Se você tem um objeto JSON grande com várias configurações aninhadas (ex: configuração de autenticação com 5 propriedades).
  • Tipagem Forte: Quando você quer garantir que todos os ambientes sigam rigorosamente uma Interface TypeScript.
  • Mocks de Funções: Em casos raros onde você troca uma função inteira dependendo do ambiente (embora isso seja melhor resolvido com Injeção de Dependência).
Imagem com o texto: Curso Angular Moderno e um homem de pele parda usando óculos com a logo do Angular atrás. Logo abaixo existe um botão com o texto "Eu quero!"

Como usar na prática

Para utilizar a Define Option, precisamos configurar o arquivo angular.json e garantir que o TypeScript reconheça essas novas variáveis globais. Vamos ver três exemplos práticos.

Passo Prévio: Declarando Tipos

Como essas variáveis não são importadas de nenhum arquivo, o TypeScript irá alertar que elas não existem.

Para corrigir isso, crie ou edite um arquivo de definição de tipos, como src/types.d.ts:

   // src/types.d.ts
declare const API_URL: string
declare const ENABLE_BETA_FEATURE: boolean
declare const APP_VERSION: string

Exemplo 1: Feature Flag com Tree-Shaking no TypeScript

Neste exemplo, vamos configurar uma lógica que só deve ser executada se a feature flag estiver ativa.

Ao realizar a verificação no TypeScript, garantimos que o código morto seja removido do bundle final se a constante for false.

No seu angular.json, localize a configuração de build e adicione o objeto define:

   "architect": {
  "build": {
    "options": {
      "define": {
        "ENABLE_BETA_FEATURE": "false"
      }
    }
  }
}

Agora, utilizamos a variável na lógica do componente:

   import { Component, signal } from '@angular/core'

@Component({
	selector: 'app-user-dashboard',
	standalone: true,
	template: `
		<h1>Dashboard</h1>
		<ul>
			@for (item of menuItems(); track item) {
				<li>{{ item }}</li>
			}
		</ul>
	`
})
export class UserDashboardComponent {
	menuItems = signal<string[]>(['Perfil', 'Configurações'])

	constructor() {
		this.initializeFeatures()
	}

	private initializeFeatures() {
		// Se ENABLE_BETA_FEATURE for false, este bloco if inteiro
		// será removido do JavaScript final pelo esbuild (Tree-shaking)
		if (ENABLE_BETA_FEATURE) {
			this.menuItems.update((items) => [...items, 'Análise Avançada (Beta)'])
			this.loadHeavyBetaAnalytics()
		}
	}

	private loadHeavyBetaAnalytics() {
		console.log('Carregando scripts pesados de análise...')
	}
}

Exemplo 2: Injeção de URL da API

Configurar a URL base da API é essencial.

Note o uso de aspas duplas e simples ("'...'") na configuração JSON para garantir que o valor seja interpretado como uma string.

No angular.json:

   "define": {
  "API_URL": "'https://api.producao.com.br'"
}

No seu serviço, utilize a variável diretamente:

   import { Injectable, inject } from '@angular/core'
import { HttpClient } from '@angular/common/http'

@Injectable({
	providedIn: 'root'
})
export class ProductService {
	private http = inject(HttpClient)

	// A variável API_URL será substituída pela string configurada
	private baseUrl = API_URL

	getProducts() {
		return this.http.get(`${this.baseUrl}/products`)
	}
}

Exemplo 3: Versionamento da Aplicação

Muitas vezes queremos mostrar a versão atual do sistema no rodapé. Podemos passar isso via define.

No angular.json:

   "define": {
  "APP_VERSION": "'1.0.5'"
}

No componente de rodapé:

   import { Component, signal } from '@angular/core'

@Component({
	selector: 'app-footer',
	standalone: true,
	template: `
		<footer>
			<p>Versão do Sistema: {{ version() }}</p>
		</footer>
	`
})
export class FooterComponent {
	// Usando signals para gerenciar o estado estático
	version = signal(APP_VERSION)
}

Sobrescrevendo Valores via CLI (ng build)

Uma das maiores vantagens dessa abordagem é a facilidade de integração com pipelines de CI/CD.

Você não precisa editar o arquivo angular.json para cada ambiente; pode passar os valores diretamente no comando de build.

Para isso, utilize a flag --define.

Isso é extremamente útil para injetar chaves de API secretas ou números de versão gerados dinamicamente pelo pipeline.

Exemplo de uso no terminal:

   # Sobrescrevendo a URL da API e a versão
ng build --define "API_URL='https://staging.api.com'" --define "APP_VERSION='2.0.0-rc.1'"

Exemplo para Feature Flags:

   # Habilitando a feature beta apenas nesta build específica
ng build --define "ENABLE_BETA_FEATURE=true"

Atenção às aspas: Ao passar strings via linha de comando, certifique-se de escapar as aspas corretamente dependendo do seu terminal (Bash, PowerShell ou CMD), garantindo que o valor final contenha as aspas necessárias para ser interpretado como string no JavaScript.

Conclusão

A utilização da Define Option no Angular moderno oferece uma maneira limpa e eficiente de lidar com constantes ambientais.

Ao substituir valores durante a compilação, ganhamos em segurança e performance, aproveitando ao máximo as capacidades dos bundlers modernos como o esbuild.

Essa abordagem simplifica a arquitetura do projeto, removendo a necessidade de importar arquivos de ambiente em todos os lugares e facilitando a gestão de funcionalidades através de Feature Flags reais.

É uma prática recomendada para projetos que buscam escalabilidade e otimização.

Assine nossa Newsletter

Receba novos posts como esse na sua caixa de e-mail!

Sobre o autor

Author's photo
Henrique Custódia Arquiteto Frontend, entusiasta Angular, cat lover, criador de conteúdo e fundador da Code Dimension!