Finalmente consegui finalizar meu primeiro post para o dev.to
Nas primeiras tentativas de entregar algo eu falhei miseravelmente pois, simplesmente, a postagem ficava gigantesca demais.
Dessa vez consegui deixar o escopo fechadinho e de uma maneira que, acredito eu, seja bem fácil de reproduzir.
Então, chega de falatório (ou escritório...) e vamos começar.
⚡ Pré requisitos
Não vamos criar nada revolucionário ou complexo demais por aqui, já que o intuito é demonstrar como validar as variáveis necessárias para a aplicação subir.
Para este tutorial, vamos precisar do Node.js instalado na máquina. Também vou usar o git para versionar os códigos. Vou partir do princípio de que estes pré-requisitos já estejam atendidos por você, caro leitor.
Estou utilizando o macOS como sistema operacional e utilizarei o terminal para executar todos os comandos. Se estiver usando Windows, sugiro fortemente que utilize o WSL.
Também estou usando o yarn como gerenciador de pacote ao invés do npm.
⚡ Tudo que vamos usar nesse projeto
- typescript
- fastify
- dotenv
- zod
- tsx
- tsup
Um detalhe: estou usando o fastify pois vou postar mais alguns materiais por aqui usando esse micro-framework. Mas o que vou aplicar aqui é totalmente compatível com o express ou com frameworks mais parrudos, como o NestJS.
⚡ Iniciando o projeto
Vamos iniciar criando um novo diretório env-validate.
Abra o terminal e execute o comando:
mkdir env-validate && \
cd env-validate
Agora vamos iniciar um projeto node com o comando:
yarn init -y
Já preparando para que os commits no git não suba o diretório node_modules e o .env, vamos criar um arquivo .gitignore na raiz do projeto:
touch .gitignore && \
echo '.env' >> .gitignore && \
echo 'node_modules' >> .gitignore
Vamos fazer um primeiro commit para facilitar a navegação no histórico de versões no futuro.
git add . && \
git commit -m "initial commit"
⚡ Iniciando o Fastify e o Typescript
Vamos iniciar um servidor usando o fastify com uma rota GET /hello para termos um mínimo de código por aqui.
Para isso vamos instalar o fastify:
yarn add fastify
Como vamos usar Typescript, vamos precisar do próprio typescript instalado.
Também vamos precisar instalar a tipagem do node (@types/node), um interpretador para os arquivos .ts (tsx) e um compilador de typescript para javascript (tsup).
yarn add -D typescript @types/node tsx tsup
Com o typescript instalado, vamos iniciar o tsconfig.json com o comando:
yarn tsc --init
Para este tutorial não vamos precisar modificar nada neste arquivo
Agora vamos gerar o arquivo app.ts, onde as configurações do fastify estarão:
mkdir -p src/infra && \
touch src/infra/app.ts
Cole o conteúdo abaixo no arquivo app.ts gerado:
import fastify, { FastifyReply, FastifyRequest } from "fastify";
const app = fastify()
app.get('/hello', (request: FastifyRequest, reply: FastifyReply) => {
return reply.status(200).send({ message: 'hello world' })
})
export { app }
O código deste arquivo criamos uma constante app que é uma instância da função fastify.
Com essa variável app, criamos uma rota GET /hello que retornará uma mensagem 'hello world' com status 200.
Agora vamos gerar o arquivo server.ts, que é o nosso entrypoint da aplicação
touch src/infra/server.ts
Cole o conteúdo abaixo no arquivo server.ts:
import { app } from "./app";
async function bootstrap() {
await app.listen({ host: '0.0.0.0', port: 3333 })
console.log('🚀 server started at port 3333')
}
bootstrap()
Explicando...
Aqui temos uma função assíncrona bootstrap que cria um servidor fastify que veio da variável 'app' importada na primeira linha.
Este servidor está subindo no endereço http://localhost:3333
Mas para este servidor funcionar, precisamos inserir um script no arquivo package.json.
Abra este arquivo e insira uma nova chave scripts:
"scripts": {
"start:dev": "tsx watch src/infra/server.ts"
}
Para subir o servidor, basta no terminal executar o comando:
yarn start:dev
O que esperamos ver no terminal é a seguinte mensagem:
Uma vez visto a bela mensagem de start do servidor, basta acessar via browser o endereço: http://localhost:3333/hello
Se tudo estiver certo, esta é a mensagem que deverá ver no seu browser
Para ver a mensagem formatada assim, estou usando a extensão JSON Viewer com tema dark no Chrome
Novo commit para finalizar esse bloco
git add . && \
git commit -m "fastify server started"
⚡ Validando as variáveis de ambiente
Se ainda estiver com o servidor executando no terminal, encerre o processo com CTRL + C ou abra uma nova aba/janela.
Agora vamos gerar um arquivo .env onde listaremos quais variáveis de ambiente são necessárias para a aplicação iniciar.
touch .env
Cole o conteúdo abaixo no arquivo .env gerado:
# API_PORT=3333
Para validar as variáveis de ambiente, vamos utilizar a biblioteca de validação zod e também vamos precisar instalar a biblioteca dotenv. Esta última nos possibilita ler as variáveis de ambiente ao importá-la no início do arquivo env.ts.
Execute no terminal:
yarn add zod dotenv
Para criar o arquivo env.ts, execute:
touch src/infra/env.ts
Cole o conteúdo abaixo no arquivo gerado:
import 'dotenv/config'
import { z } from 'zod'
const envSchema = z.object({
API_PORT: z.coerce.number()
})
const getEnv = envSchema.safeParse(process.env)
if (!getEnv.success) {
const errorMessage = 'load environment failed'
console.error(errorMessage, getEnv.error.format())
throw new Error(errorMessage)
}
export const env = getEnv.data
Vamos repassar o que foi colado:
Primeiro, importamos o dotenv/config para realizar a leitura do arquivo .env. Em seguida importamos de dentro do zod o z.
O zod trabalha com uma série de parâmetros e aqui estamos usando o object para gerar um schema com as variáveis necessárias para a aplicação iniciar.
Neste caso declaramos que a propriedade API_PORT é do tipo z.coerce.number().
Tudo que é importado das variáveis de ambiente do arquivo .env é interpretado como texto. O termo coerce utilizando antes do .number() é uma forma de dizer para o zod realizar uma conversão do que está chegando (string) para o formato number.
Em seguida criamos uma variável getEnv onde passamos o envSchema.safeParse(process.env)
O comando safeParse valida se o que está sendo repassado pelo process.env atende ao que foi especificado no schema.
Como resultado, a variável getEnv terá uma propriedade .success booleana.
Logo abaixo estamos checando se este .success falhou e, caso tenha falhado, interrompemos o script com um erro load environment failed.
Se a validação passar, ou seja, se o .success for verdadeiro, exportamos a variável env com os dados obtidos do getEnv.data
Chegou a hora de testar:
Volte ao arquivo server.ts e mude a propriedade port para que este leia o env.API_PORT exportado nos passos anteriores.
o arquivo server.ts ficará assim
import { app } from "./app";
import { env } from "./env";
async function bootstrap() {
await app.listen({ host: '0.0.0.0', port: env.API_PORT })
console.log(`🚀 server started at port ${env.API_PORT}`)
}
bootstrap()
Aproveitei e também modifiquei a mensagem do console para que a porta passada nas variáveis seja exibida no terminal.
Agora vamos rodar a aplicação para ver o que acontece:
yarn start:dev
E... eitaaaa, erro!
Calma, era o esperado :)
Como pode observar no terminal, temos a seguinte mensagem de erro no topo da execução:
Essa mensagem está aqui porque criamos o nosso arquivo .env com a variável API_PORT comentada de propósito.
Para resolver isso, pare a execução do servidor com CTRL + C, volte no arquivo .env e remova o # da primeira linha.
Seu arquivo .env. deve ficar assim:
API_PORT=3333
Execute a aplicação novamente e tudo deve dar certo agora
yarn start:dev
O esperado é você ver o seu terminal com a mensagem:
Tenho certeza (será?) que tudo deu certo.
Então vamos criar um novo arquivo .env.example para que, ao clonar este repositório, seja possível iniciar o projeto apenas renomeando para .env
cp .env .env.example
E, por último nessa sessão, vamos commitar a coisa toda e partir para a última parte.
git add . && \
git commit -m "environment variables validated"
⚡ Gerando build do projeto
Para finalizar este tutorial, vamos configurar o script que executará o build da aplicação e o start da versão final em javascript.
Para isso, vá até o arquivo package.json e adicione os scripts abaixo:
"scripts": {
...
"prebuild": "tsc --noEmit",
"build": "tsup src --out-dir build",
"start": "node build/server.js"
}
Explicando mais uma vez...
O script build utiliza a biblioteca tsup para transpilar os códigos typescript para javascript usando como diretório build como saída.
Porém, antes de executar o transpile, o prebuild é chamado automaticamente para que o typescript faça o transpile por conta própria apenas em memória. Caso algo falhe, o build é interrompido nessa fase.
Se nada falhar, o diretório build é populado com os arquivos javascript nos quais o Node.JS será capaz de executar nativamente.
O script start executa o servidor já com o arquivo compilado.
Este é o comando que será utilizado caso esta aplicação esteja publicada.
Para testar tudo, basta executar no terminal:
yarn build
Você deverá ver algo como:
Agora basta executar:
yarn start
E verá no seu terminal...
Novo commit e fim do tutorial
git add . && \
git commit -m "app build added"
Ufaaa...
Assim encerro meu primeiro tutorial postado por aqui.
Toda e qualquer variável de ambiente necessária para a aplicação poderá ser listada no schema gerado no arquivo env.ts.
Dessa forma, caso alguma delas não seja declarada no ambiente onde a aplicação será publicada.
Por exemplo: variáveis do banco de dados, da AWS ou de qualquer outro provider, variáveis de tokens JWT e por aí vai.
Basta adicionar o que precisa no schema e está garantido que a aplicação se quer irá iniciar na falta de alguma delas.
Espero que tenham gostado.
Para acessar o repositório do projeto no github, clique aqui
Até o próximo ;)






Top comments (0)