Node.js
Runtime JavaScript no servidor. Event loop single-threaded + I/O não bloqueante. Base de toda a stack JS/TS no backend.
Instalação — NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install --lts # instalar LTS
nvm install 22 # versão específica
nvm use 22
nvm alias default 22
nvm ls # listar instaladas
Módulos CJS / ESM
CommonJS
module.exports = { foo, bar }
const { foo } = require('./utils')
const express = require('express')
ES Modules (moderno)
// package.json: "type": "module"
export const foo = () => {}
export default function bar() {}
import { foo } from './utils.js' // extensão obrigatória em ESM
import bar from './bar.js'
Interop / __dirname em ESM
import { fileURLToPath } from 'url'
import path from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
// importar CJS em ESM
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const pkg = require('./package.json')
Event Loop
timers
setTimeout, setInterval
pending callbacks
callbacks de I/O do ciclo anterior
idle / prepare
uso interno
poll
buscar novos eventos de I/O (espera aqui)
check
setImmediate
close callbacks
socket.on('close'), etc
Microtasks (process.nextTick e Promises) rodam entre cada fase, antes da próxima.
setTimeout(() => console.log('timeout'), 0)
setImmediate(() => console.log('immediate'))
Promise.resolve().then(() => console.log('promise'))
process.nextTick(() => console.log('nextTick'))
// output:
// nextTick ← antes de qualquer fase
// promise ← microtask
// timeout
// immediate
fs — File System
import { readFile, writeFile, readdir, stat, mkdir, unlink } from 'fs/promises'
const content = await readFile('./data.txt', 'utf-8')
await writeFile('./output.txt', 'conteúdo')
const files = await readdir('./src')
const info = await stat('./file.txt') // .size, .mtime, .isDirectory()
await mkdir('./nova-pasta', { recursive: true })
await unlink('./arquivo.txt')
// streams (grandes arquivos)
import { createReadStream, createWriteStream } from 'fs'
createReadStream('./grande.csv').pipe(createWriteStream('./output.csv'))
path / os
import path from 'path'
path.join('/foo', 'bar', 'baz.txt') // /foo/bar/baz.txt
path.resolve('src', 'index.js') // /cwd/src/index.js
path.dirname('/foo/bar.txt') // /foo
path.basename('/foo/bar.txt') // bar.txt
path.extname('/foo/bar.txt') // .txt
import os from 'os'
os.cpus().length // núcleos
os.totalmem() // bytes
os.homedir() // /home/user
os.platform() // linux / darwin / win32
http
import http from 'http'
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ hello: 'world' }))
})
server.listen(3000)
Na prática, use Express ao invés de http direto.
events
import { EventEmitter } from 'events'
const emitter = new EventEmitter()
emitter.on('data', (payload) => console.log(payload))
emitter.once('connect', () => console.log('só uma vez'))
emitter.emit('data', { value: 42 })
emitter.removeAllListeners('data')
crypto
import crypto from 'crypto'
crypto.randomUUID() // UUID v4
crypto.randomBytes(32).toString('hex') // token aleatório 64 chars
const hash = crypto.createHash('sha256').update('texto').digest('hex')
const hmac = crypto.createHmac('sha256', 'chave').update('msg').digest('hex')
child_process
import { exec, spawn } from 'child_process'
import { promisify } from 'util'
const execAsync = promisify(exec)
const { stdout } = await execAsync('git log --oneline -5')
// spawn — stream de output
const proc = spawn('tail', ['-f', '/var/log/app.log'])
proc.stdout.on('data', data => process.stdout.write(data))
proc.on('close', code => console.log(`exit ${code}`))
Streams
import { Transform, pipeline } from 'stream'
import { promisify } from 'util'
const pipelineAsync = promisify(pipeline)
const upper = new Transform({
transform(chunk, encoding, callback) {
callback(null, chunk.toString().toUpperCase())
}
})
await pipelineAsync(readableSource, upper, writableDestination)
// ler arquivo linha por linha
import { createInterface } from 'readline'
import { createReadStream } from 'fs'
const rl = createInterface({ input: createReadStream('grande.csv') })
for await (const line of rl) {
process(line)
}
npm / package.json
npm init -y
npm install express
npm install -D typescript @types/node
npm install -g nodemon
npm uninstall pacote
npm update
npm audit fix
npm list --depth=0
npm run dev
{
"name": "meu-projeto",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js",
"build": "tsc",
"test": "jest"
},
"dependencies": { "express": "^4.18.2" },
"devDependencies": { "nodemon": "^3.0.0" },
"engines": { "node": ">=18.0.0" }
}
Async / Await
// básico
const data = await fetch('https://api.example.com/users')
const json = await data.json()
// paralelo
const [users, posts] = await Promise.all([db.getUsers(), db.getPosts()])
// race (timeout)
const result = await Promise.race([
fetch('/api/data'),
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 5000))
])
// allSettled — não rejeita se um falhar
const results = await Promise.allSettled([p1, p2, p3])
results.forEach(r => {
if (r.status === 'fulfilled') console.log(r.value)
else console.error(r.reason)
})
Erros
// erro customizado
class AppError extends Error {
constructor(message, statusCode = 500) {
super(message)
this.name = 'AppError'
this.statusCode = statusCode
Error.captureStackTrace(this, this.constructor)
}
}
throw new AppError('Not found', 404)
// globais — registrar antes do app iniciar
process.on('uncaughtException', (err) => {
console.error('Uncaught:', err)
process.exit(1)
})
process.on('unhandledRejection', (reason) => {
console.error('Unhandled:', reason)
process.exit(1)
})
Variáveis de Ambiente
# .env
PORT=3000
DATABASE_URL=postgresql://...
JWT_SECRET=segredo_forte
NODE_ENV=development
// Node 20.6+ — nativo
// node --env-file=.env src/index.js
// dotenv (versões anteriores)
import 'dotenv/config'
const port = process.env.PORT ?? 3000
const dbUrl = process.env.DATABASE_URL
if (!dbUrl) throw new Error('DATABASE_URL is required')
Worker Threads
CPU-intensivo sem bloquear o event loop.
import { Worker, isMainThread, parentPort, workerData } from 'worker_threads'
if (isMainThread) {
const worker = new Worker('./worker.js', { workerData: { nums: [1,2,3] } })
worker.on('message', result => console.log(result))
} else {
const sum = workerData.nums.reduce((a, b) => a + b, 0)
parentPort.postMessage(sum)
}
Cluster
Usar todos os núcleos da CPU com múltiplos processos.
import cluster from 'cluster'
import os from 'os'
if (cluster.isPrimary) {
for (let i = 0; i < os.cpus().length; i++) cluster.fork()
cluster.on('exit', () => cluster.fork()) // reiniciar se morrer
} else {
// servidor aqui
app.listen(3000)
}
TypeScript
npm install -D typescript @types/node tsx
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true
}
}
// dev: tsx --watch src/index.ts
// prod: tsc && node dist/index.js
process
process.env // variáveis de ambiente
process.argv // args CLI (argv[0]=node, argv[1]=script)
process.cwd() // diretório atual
process.pid // PID
process.version // versão Node.js
process.platform // 'linux' | 'darwin' | 'win32'
process.memoryUsage() // heapUsed, heapTotal, rss
process.exit(0) // 0=sucesso, 1=erro
Ferramentas Essenciais
| Pacote | Uso |
|---|---|
| nodemon | reiniciar ao salvar (dev) |
| tsx | executar TypeScript diretamente |
| dotenv | carregar .env |
| zod | validação de schema runtime |
| pino | logging estruturado de alta performance |
| winston | logging com múltiplos transports |
| jest / vitest | testes unitários e integração |
| supertest | testar HTTP sem subir servidor |
| pm2 | process manager em produção |