E aí 🚀
Quando estamos construindo aplicações backend, é crucial lidar com informações sensíveis, como URLs para conexão com o banco de dados, chaves secretas e outras variáveis críticas.
E como não queremos deixar essas informações expostas, recorremos ao uso variáveis de ambiente para proteger nossa aplicação e evitar exposições indesejadas.
Nesse artigo, apresentarei uma forma que tenho usado em minhas aplicações Node e Nest.
Passo 1: Instalação dos pacotes - Node
Para projetos node, vamos precisar dos seguintes pacotes, zod e dotenv:
npm install zod dotenv
Dependendo da sua entrutura de pastas, costumo criar uma pasta para config e, dentro  dela, um arquivo chamado env.ts.
Passo 2: Criação do schema
O que vamos fazer agora é utilizar o zod para validação de nossas variáveis de ambiente.
import 'dotenv/config'
import { z } from 'zod'
const environmentSchema = z.object({
  NODE_ENV: z.enum(['dev', 'test', 'prod']).default('dev'),
  PORT: z.coerce.number().default(3333),
  DB_URL: z.string(),
})
Anteriormente, importamos o zod e dotenv e, em seguida, criamos um esquema (schema) com base nas variaveis que serão utilizadas em nossa aplicação. Nesse caso,estamos considerando apenas as variáveis PORT/ DB_URL e NODE_ENV.
Mas como validamos esse esquema para nossas variaveis de ambiente?
// Código acima
const _env = environmentSchema.safeParse(process.env) //
if (!_env.success) {
  console.error('Invalid environment variables', _env.error.format())
  throw new Error('Invalid environment variables')
}
export const env = _env.data
Como o process.env é retornado como um objeto, o que permite validar esse objeto em conformidade com o enviromnentSchema e, caso não cumpra o "contrato" será lançado um erro.
Passo 3: Utilização
A utilizaração é bem simples:
import express from 'express'
import { env } from '../config/env' // caminho para a pasta em seu projeto
const app = express()
app.listen(env.PORT, () => {
  console.log('server running')
})
Agora, vamos para o queridinho Nest.
Passo 1: Instalação dos pacotes - Nest
Nesse exemplo, criaremos um modulo, um serviço e um esquema para isso mas antes será necessário instalar o pacote abaixo:
npm i --save @nestjs/config
Passo 2: Criação do schema
Para o arquivo env, teremos algumas mudanças:
//env.ts
import { z } from 'zod';
export const envSchema = z.object({
  DATABASE_URL: z.string().url(),
  PORT: z.coerce.number().optional().default(3000),
});
export const validateEnv = (env: Record<string, any>) => {
  const _env = envSchema.safeParse(process.env); //
  if (!_env.success) {
    console.error('Invalid environment variables');
    throw new Error('Invalid environment variables');
  }
  return _env.data;
};
export type Env = z.infer<typeof envSchema>;
Passo 3: Trabalhando com módulos
Dentro do Nest, trabalhar com modulos e services é essencial. O modulo de env precisa ser visivel por toda aplicação, e o serviço precisa ser injetado em qualquer modulo em que for chamado.
Como assim injetar? serviço? Se está meio confuso, recomendo consultar a documentação do Nest antes de prosseguir com o artigo.
// env.service
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Env } from './env';
@Injectable()
export class EnvService {
  constructor(private configService: ConfigService<Env, true>) {}
  get<T extends keyof Env>(key: T) {
    return this.configService.get(key, { infer: true });
  }
}
No exemplo acima, é um serviço no nest, fazendo o uso do padrão de decorators conseguimos tornar esse serviço acessível e utilizável em diferentes partes da aplicação.
// env.module.ts
import { Module } from '@nestjs/common';
import { EnvService } from './env.service';
@Module({
  providers: [EnvService],
  exports: [EnvService],
})
export class EnvModule {}
Conforme mencionado anteriormente, é crucial informar à nossa aplicação que esse módulo deve ser global. Para realizar essa configuração, vamos até o módulo principal da aplicação:
// app.module.ts
import { ConfigModule } from '@nestjs/config';
import { envSchema } from './infra/env';
@Module({
  imports: [
    ConfigModule.forRoot({
      validate: (env) => validateEnv(env),
      isGlobal: true,
    }),
  ],
})
export class AppModule {}
Passo 4: Utilizando o serviço de variáveis
Mas e o arquivo main.ts? como teria acesso as variáveis? como consigo iniciar minha aplicação na porta que eu desejo?
Ao utilizar o app, podemos configurar e inicializar a aplicação conforme suas necessidades incluindo a porta que desejamos utilizar:
//main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const configService = app.get(EnvService); //Acessando o service
  const port = configService.get('PORT'); //Pegando a variável {PORT}
  await app.listen(port);
}
bootstrap();
Por fim, caso seja necessário acessar alguma variável, podemos realizar a injeção desse serviço no módulo em que é necessário o acesso. Assim conseguimos garantir o uso variáveis.
// database.module.ts
import { EnvModule, EnvService } from '../env';
@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      imports: [EnvModule],
      inject: [EnvService],
      useClass: TypeOrmConfigService,
    }),
  ],
})
export class DatabaseModule {}
//typeorm.service.ts
@Injectable()
export class TypeOrmConfigService implements TypeOrmOptionsFactory {
  constructor(private config: EnvService) {}
  createTypeOrmOptions(): TypeOrmModuleOptions {
    return {
      type: 'postgres',
      host: this.config.get('DB_HOST'),
      port: this.config.get('DB_PORT'),
      username: this.config.get('DB_USERNAME'),
      password: this.config.get('DB_PASSWORD'),
      database: this.config.get('DB_NAME'),
      entities,
      synchronize: true,
      dropSchema: true,
      logging: true,
    };
  }
}
Conclusão
Para concluir, o objetivo deste artigo é apresentar uma abordagem para lidar com variáveis de ambiente, tanto no Nest quanto no Node. 
Espero que alguma informaçõe fornecida seja útil e que agora você possa utilizar ou aprimorar esse modo em suas aplicações. Se houver dúvidas ou sugestões, sinta-se à vontade para discutir! 
 
 
              
 
    
Top comments (0)