NestJS: lendo as variáveis de ambiente com o ConfigService
Aprenda a usar o ConfigModule e o ConfigService do framework NestJS para organizar as variáveis de configuração da sua aplicação, incluindo as variáveis de ambiente. Descubra como gerenciar configurações para diferentes ambientes e trazer mais flexibilidade ao seu projeto.
Para quem não conhece, NestJS é um framework para a construção de aplicações backend que rodam com Node.js. O TypeScript é a linguagem principal, e sua sintaxe se parece com a do framework Angular. Particularmente, também acho um pouco parecido com o Spring Boot do Java, mas é uma opinião totalmente pessoal.
Breve introdução às variáveis de ambiente
Independente da linguagem de programação e framework escolhido, é comum que nossas aplicações rodem em diferentes ambientes, como no seu computador durante o desenvolvimento, em um ambiente de teste ou em produção. Cada ambiente pode ter suas particularidades. Por exemplo, a string de conexão com o banco de dados no seu computador é diferente daquela usada no servidor de produção.
Para lidar com essas diferenças, usamos variáveis de ambiente, que são basicamente pares de chave e valor ajustados de acordo com o ambiente. Essas variáveis são configuradas de maneira específica para cada sistema operacional e linguagem, e são definidas antes da execução da aplicação. Uma vez estabelecidas, a aplicação pode acessá-las durante sua execução para configurar parâmetros, como a string de conexão com o banco de dados, endereços de APIs externas, configuração de log e outros valores que podem variar conforme o ambiente onde a aplicação está rodando.
Para mostrar este cenário na prática, será criado a seguir um projeto utilizando NestJS. Em seguida, será apresentado como utilizar o ConfigModule
e ConfigService
para criar um objeto de configuração, onde parte dos valores podem ser obtidos através da leitura de variáveis de ambiente.
Criando o projeto de estudo
Primeiramente vamos criar um projeto do zero para ser o cenário de estudo:
# Conferindo a versão do Node.js:
node --version
# v20.18.0
# Instalando o CLI do NestJS de forma global:
npm i -g @nestjs/cli
# Utilizei a versão 10.4.5 do NestJS. Para verificar
# use o comando `nest --version`
# Criando o projeto:
nest new usando-config-service
# Após executar o comando acima, o CLI irá
# perguntar qual é sua escolha para gerenciador
# de pacotes. Neste exemplo eu selecionei o NPM.
# Após criado, basta acessar o diretório do projeto...
cd usando-config-service
# ...e executar:
npm run start:dev
Após executar os comandos acima, a aplicação será executada na porta padrão, 3000. Através do navegador ou qualquer outro cliente TCP, podemos efetuar um teste consultando o endereço http://localhost:3000
.
# Exemplo utilizando o curl
curl http://localhost:3000
O resultado obtido será Hello World!
.
Instalando o pacote nestjs/config
Agora que o projeto está criado, vamos instalar a dependência @nestjs/config
com o comando:
npm install @nestjs/config
nestjs/config
utiliza o pacote dotenv
.https://docs.nestjs.com/techniques/configuration#installation
Configurando o ConfigModule no NestJS
Com o pacote @nestjs/config
instalado, precisamos configurar o ConfigModule
na aplicação. Ele será responsável por carregar as variáveis de ambiente do arquivo .env
(será criado a seguir) e torná-las acessíveis através do ConfigService
.
Importando e configurando o ConfigModule
no AppModule
:
Feito a alteração acima, foi criado o arquivo.env
na raiz do projeto com o seguinte conteúdo:
Usando o ConfigService
para ler as variáveis de ambiente
Com o ConfigModule
configurado, podemos injetar o ConfigService
nas classes que desejarmos, por exemplo, vamos alterar o AppController
para ler algumas variáveis de ambiente e entregá-las na resposta da requisição:
Ao executarmos o projeto e acessarmos o endereço localhost:3000
, a resposta será a exatamente os valores lidos das variáveis de ambiente declaradas no arquivo .env
:
{
"valor1": "AAA",
"valor2": "123",
"valor3": "true",
"valor4": "2024-10-21T12:00:00.000Z"
}
Aqui vale destacar algumas observações:
- o
ConfigService
permite a leitura das variáveis de ambiente de forma similar aoprocess.env.VARIAVEL
, então qual a vantagem? - perceba que por padrão, as variáveis de ambiente são simplesmente um conjunto de chaves e valores do tipo
string
, são textos. Observe que mesmo adicionando a tipagem no métodoget<>
, não acontece uma conversão de tipo. Como tratar este cenário e obter os dados com a tipagem desejada?
Para responder as duas dúvidas, vamos melhorar a estrutura criando um arquivo de configuração, como se fosse uma camada entre as variáveis de ambiente e o ConfigService
.
Como criar configurações tipadas com o ConfigModule no NestJS
O NestJS possibilita a criação de um arquivo de configuração para organizar melhor as variáveis que nossa aplicação irá utilizar, dentre essas variáveis também podemos incluir as de ambiente.
For more complex projects, you may utilize custom configuration files to return nested configuration objects. This allows you to group related configuration settings by function (e.g., database-related settings), and to store related settings in individual files to help manage them independently.
https://docs.nestjs.com/techniques/configuration#custom-configuration-files
Para isto, podemos criar um arquivo chamado configuration.ts
, o nome do arquivo é opcional, e adicionar o seguinte conteúdo:
Perceba que podemos declarar absolutamente qualquer coisa neste arquivo, é basicamente um objeto com valores para configuração da aplicação. Também podemos aproveitar e efetuar a conversão das variáveis de ambiente para o tipo que esperamos, por exemplo, parseInt(process.env.VALOR2, 10)
.
Por fim, passamos o conteúdo deste arquivo como referência no ConfigModule
na propriedade load
:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import configuration from './configuration';
@Module({
imports: [
ConfigModule.forRoot({
// O parâmetro isGlobal: true garante que o
// ConfigService esteja disponível em toda a
// aplicação, sem a necessidade de importá-lo
// manualmente em cada módulo.
isGlobal: true,
load: [configuration],
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Feito isto, podemos utilizar o método get
do ConfigService
passando como parâmetro da consulta a estrutura criada no arquivo de configuração, veja a seguir:
Observe no resultado abaixo que valor2
é um número, valor3
é um booleano, e valor4
é uma data. Vale lembrar que, no JSON, objetos Date
são representados como strings no formato ISO-8601. Já o valor5
é o número 5, que corresponde ao valor padrão caso a chave minhaAplicacao.valor_inexistente
não seja encontrada nas configurações.
{
"valor1": "AAA",
"valor2": 123,
"valor3": true,
"valor4": "2024-10-21T12:00:00.000Z",
"valor5": 5
}
Assim melhoramos a organização das variáveis que nossa aplicação irá utilizar, além de obtermos estes dados já em sua tipagem correta. Essa é uma das vantagens do ConfigService
em relação ao process.env
.
Como trabalhar com vários arquivos de configuração .env
Eventualmente teremos em nosso ambiente de desenvolvimento mais de um arquivo .env
, por exemplo, development.env
e local.env
. Estes nomes não são regras, são apenas exemplos. Uma das formas de chavearmos entre um arquivo e outro é utilizando a propriedade envFilePath
do ConfigModule
. Essa propriedade permite a declaração de uma lista de arquivos. O ConfigModule
irá ler da esquerda para direita mantendo como prioridade o que vem primeiro. Por exemplo, observe os dois arquivos .env
a seguir:
Declarando os dois arquivos .env
no ConfigModule
:
Ao executar o projeto, o seguinte resultado será obtido ao consultar o endereço localhost:3000
:
{
"valor1": "AAA",
"valor2": 111111123,
"valor3": true,
"valor4": "2024-10-21T12:00:00.000Z",
"valor5": 5
}
Perceba que valor2
não existe no arquivo local.env
, mas existe no development.env
. Então o ConfigService
manteve o valor do segundo arquivo visto que ele não foi encontrado no primeiro. Já valor1
, valor3
e valor4
foram lidos do arquivo local.env
, que foi declarado antes do development.dev
. É crucial entender essa regra de precedência.
Escolhendo qual arquivo utilizar
Uma forma simples de definir qual arquivo você vai utilizar é através de variáveis de ambiente. Podemos preencher uma variável de ambiente, por exemplo NODE_ENV
, com algum valor na declaração do script dentro do package.json
e usar este valor para definir qual arquivo será carregado:
Observação: caso esteja executando a aplicação no CMD do Windows, utilize o seguinte:
Ao executar a aplicação com o comando npm run start:local
, o arquivo local.env
será utilizado. Já ao executar npm run start:dev
, o arquivo development.env
será carregado. Caso a variável NODE_ENV
não esteja definida, o arquivo .env
será usado como padrão.
.env
! É recomendável a não inclusão do arquivo .env no seu repositório. Incluir o arquivo.env
no repositório pode levar a vazamentos de dados e comprometer sua aplicação. Como alternativa podemos destacar a utilização de um serviço especializado como o Azure Key Vault ou a criação manual do arquivo .env
dentro dos seus servidores.Considerações
O ConfigService
do NestJS facilita o gerenciamento de variáveis de configuração da aplicação, dentre elas, as variáveis de ambiente. Com ele, podemos centralizar todas as configurações da aplicação, garantindo que nossa aplicação se comporte de forma consistente em diferentes ambientes.
Abaixo deixo o link da documentação oficial: