Meu setup Node.js + Typescript para backend
30 de agosto de 2023
Aprenda a configurar um ambiente completo para desenvolver seus projetos backends com Node.js.
Introdução
Configurar um bom ambiente de desenvolvimento para backend com Node.js não é uma coisa tão trivial como muitos artigos mostram. Por isso, neste post vou fazer um passo-a-passo de como e por quê usar as configurações que uso, lembrando que é apenas a minha opinião baseada em minhas experiências.
Pré-requisitos
NVM
Node Version Manager é essencial para gerenciar versões do node diferentes para cada projeto. Assim, para cada terminal aberto é possível usar uma versão diferente ao mesmo tempo. Disponível para todos os sistemas operacionais.
Git
Git é essencial para gerenciar o código fonte dos seus projetos. Disponível para todos os sistemas operacionais.
Visual Studio Code
Visual Studio Code será o editor de código que usaremos. Disponível para todos os sistemas operacionais.
Configurando o Visual Studio Code
Perfil
Para começar iremos criar um perfil de desenvolvimento para o nosso setup:
Manage > Profiles > Create Profile
Profile name: Backend (Node.js)
[x] Settings
[x] Keyboard Shortcuts
[x] User Snippets
[x] User Tasks
[x] Extensions
Create
Fonte
Fira Code: fonte monoespaçada com ligaduras. Basta acessar o link, baixar e instalar em seu sistema operacional.
Acesse o arquivo de configuração em File > Preferences > Settings > Open Settings (JSON) e adicione as seguintes linhas:
"editor.fontFamily": "Fira Code",
"editor.fontLigatures": true,
"editor.fontWeight": 500,
"editor.fontSize": 16,
"editor.tabSize": 2
Formatação
Acesse o arquivo de configuração em File > Preferences > Settings > Open Settings (JSON) e adicione as seguintes linhas:
"editor.guides.bracketPairs": true,
"editor.renderWhitespace": "boundary",
"editor.codeActionsOnSave": { "source.fixAll.eslint": true }
Temas
Acesse as extensões e adicione:
- One Dark Pro (binaryfy)
- Material Icon Theme (Philipp Kief)
Acesse o arquivo de configuração em File > Preferences > Settings > Open Settings (JSON) e adicione as seguinte linha:
"workbench.colorTheme": "One Dark Pro Darker",
"workbench.iconTheme": "material-icon-theme"
Outras configurações
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
"diffEditor.ignoreTrimWhitespace": false
Snippets
Crie um snippet novo em:
Manage > User Snippets > New Global Snippets file... New Snippets
Snippet name: Jest Snippets
Enter
Adicione o seguinte código:
{
"Jest Describe Template": {
"scope": "javascript, typescript",
"prefix": "describe",
"body": [
"describe('$1', () => {",
" test('$2', () => {",
" })",
"})",
""
]
},
"Jest Describe Async Template": {
"scope": "javascript, typescript",
"prefix": "describe-async",
"body": [
"describe('$1', () => {",
" test('$2', async () => {",
" })",
"})",
""
]
},
"Jest Test Template": {
"scope": "javascript, typescript",
"prefix": "test",
"body": [
"test('$2', () => {",
"})",
""
]
},
"Jest Test Async Template": {
"scope": "javascript, typescript",
"prefix": "test-async",
"body": [
"test('$2', async () => {",
"})",
""
]
}
}
Extensões
Acesse as extensões e adicione:
- Draw.io Integration (Henning Dieterichs)
- EditorConfig for VS Code (EditorConfig)
- ESLint (Microsoft)
Projeto
EditorConfig
Crie um arquivo .editorconfig na raíz do projeto com o conteúdo:
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
Este arquivo normaliza mantém o estilo do código entre os vários editores e IDEs.
NVMRC
Crie um arquivo .nvmrc na raíz do projeto com o conteúdo:
18.17.1
Este arquivo define a versão do node correta para o funcionamento do projeto. No momento da criação deste artigo, a versão do node era essa.
Iniciar projeto
npm init -y
Configurar Husky
Instale o husky através do comando recomendado aqui:
npx husky-init && npm install
Crie um arquivo .gitignore na raíz do projeto com o conteúdo:
node_modules
Altere o script test do arquivo package.json para:
"test": ""
Instale o git commit linter para padronizar as mensagens de commit:
npm i -D git-commit-msg-linter
Crie um hook para o commit-msg:
npx husky add .husky/commit-msg '.git/hooks/commit-msg $1'
Configurar Typescript
npm i -D typescript ts-node-dev @types/node dotenv
Crie um arquivo tsconfig.json na raíz do projeto com o conteúdo:
{
"compilerOptions": {
"outDir": "./dist",
"rootDirs": [
"src",
"tests"
],
"strict": true,
"target": "ES2021",
"module": "CommonJS",
"allowJs": true,
"removeComments": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strictPropertyInitialization": false,
"sourceMap": true,
"baseUrl": "src",
"paths": {
"@/tests/*": [
"../tests/*"
],
"@/*": [
"*"
]
}
},
"include": [
"src",
"tests"
]
}
Esse arquivo configura o Typescript com algumas opções importantes:
- Define a pasta compilada para ‘dist’
- Define o ES2021 como o ecmascript padrão
- Define duas pastas bases:
- src: arquivos fontes
- tests: arquivos para testes
- Define 2 alias de caminho:
- @: aponta para o diretório src/
- @/tests: aponta para o diretório tests/
Configurar ESLint com JavaScript Standard Style
Eu gosto do guia de estilo Standard, porém fique à vontade para usar outro ou nenhum. Só lembrando que quando temos um time de desenvolvimento, é bom usar algum estilo para a padronização do código.
npm i -D eslint @typescript-eslint/eslint-plugin eslint-config-standard-with-typescript
Crie um arquivo .eslintignore na raíz do projeto com o conteúdo:
node_modules
Crie um arquivo .eslintrc.json na raíz do projeto com o conteúdo:
{
"extends": "standard-with-typescript",
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": { }
}
Esse arquivo define o Standard para funcionar com o Typescript.
Configurar Jest
npm i -D jest jest-mock-extended ts-jest @types/jest
Crie um arquivo jest.config.js na raíz do projeto com o conteúdo:
module.exports = {
collectCoverageFrom: [
'<rootDir>/src/**/*.ts',
'!<rootDir>/src/**/index.ts',
'!**/*.d.ts'
],
coverageDirectory: 'coverage',
testEnvironment: 'node',
reporters: ['default'],
coverageReporters: ['lcov', 'clover', 'text-summary', 'text', 'cobertura'],
moduleNameMapper: {
'@/tests/(.*)': '<rootDir>/tests/$1',
'@/(.*)': '<rootDir>/src/$1'
},
transform: {
'\\.ts$': 'ts-jest'
},
roots: [
'<rootDir>/src',
'<rootDir>/tests'
],
clearMocks: true
}
Esse arquivo configura o Jest com algumas opções importantes:
- Ignora a cobertura dos arquivos index.ts (geralmente usado para exportar módulos)
- Ignora a cobertura dos arquivos d.ts (arquivos de declaração de tipos)
- Mapeia o root @ para as respectivas pastas
Crie um arquivo jest.unit.config.js na raíz do projeto com o conteúdo:
module.exports = {
...require('./jest.config.js'),
testMatch: ['**/*.spec.ts']
}
Este arquivo testa apenas arquivos do tipo spec.ts
Crie um arquivo jest.integration.config.js na raíz do projeto com o conteúdo:
module.exports = {
...require('./jest.config.js'),
testMatch: ['**/*.test.ts']
}
Este arquivo testa apenas arquivos do tipo test.ts
Adicione os seguintes scripts no arquivo package.json:
"test": "jest --passWithNoTests --no-cache --runInBand",
"test:unit": "npm t -- --watch --config ./jest.unit.config.js",
"test:integration": "npm t -- --watch --config ./jest.integration.config.js",
"test:coverage": "npm t -- --coverage"
Adicione a pasta coverage nos arquivos .gitignore e .eslintignore
node_modules
coverage
Crie um hook para o pre-push:
npx husky add .husky/pre-push 'npm run test:coverage'
Configurar Lint Staged
npm i -D lint-staged
Adicione o seguintes script no arquivo package.json:
"test:staged": "npm t -- --findRelatedTests"
Crie um arquivo .lintstagedrc.json na raíz do projeto com o conteúdo:
{
"*.ts": [
"eslint 'src/** --fix",
"npm run test:staged"
]
}
Altere o arquivo .husky/pre-commit para:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
Esse arquivo irá formatar e rodar os testes relacionados aos arquivos em stage antes do commit.
Configurar Atualizações
npm i -D npm-check-updates
Adicione o seguinte script no arquivo package.json:
"update": "ncu --color --interactive && npm i"
Esse script irá verificar se existem pacotes a serem atualizados.
Depuração
Crie um arquivo .vscode/launch.json com o conteúdo:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Jest Tests",
"runtimeArgs": [
"--inspect-brk",
"${workspaceRoot}/node_modules/.bin/jest",
"--runInBand"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"type": "node",
"request": "launch",
"name": "Debug Dev",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "npm",
"runtimeArgs": ["run-script", "dev"]
}
]
}
Serão criadas duas tarefas. Basta colocar um breakpoint na linha desejada e executar:
- Debug Jest Tests: depuração em ambiente de testes
- Debug Dev: depuração em ambiente de desenvolvimento
Build
npm i module-alias
npm i -D @types/module-alias rimraf
Crie um arquivo tsconfig-build.json com o conteúdo:
{
"extends": "./tsconfig.json",
"exclude": [
"tests"
]
}
Esse arquivo irá ignorar todos os arquivos da pasta tests no momento da compilação
Adicione a pasta dist nos arquivos .gitignore e .eslintignore
node_modules
coverage
dist
Adicione o seguinte script no arquivo package.json:
"build": "rimraf dist && tsc -p tsconfig-build.json"
Aplicação Hello World
Crie os seguintes arquivos dentro da pasta src do seu projeto:
// index.ts
import './alias'
import { hello } from '@/start'
hello()
// alias.ts
import { addAlias } from 'module-alias'
import { resolve } from 'path'
addAlias('@', resolve(process.env.TS_NODE_DEV === 'true' ? 'src' : 'dist'))
export const hello = (): void => {
console.log('Hello World!')
}
Adicione os seguintes scripts no arquivo package.json:
"dev": "ts-node-dev --respawn --transpile-only --ignore-watch node_modules --clear -r dotenv/config src/index.ts",
"start": "node dist/index.js"
Para rodar a aplicação em modo desenvolvimento, execute:
npm run dev
Para rodar a aplicação em modo produção, execute:
npm run build && npm start
Pronto! Agora você já tem seu ambiente de desenvolvimento configurado
Todo o código fonte deste artigo pode ser encontrado em https://github.com/guilhermenicolini/setup-node-typescript