Guides
Fundamentos ▾
Versionamento ▾
Deploy ▾

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

PacoteUso
nodemonreiniciar ao salvar (dev)
tsxexecutar TypeScript diretamente
dotenvcarregar .env
zodvalidação de schema runtime
pinologging estruturado de alta performance
winstonlogging com múltiplos transports
jest / vitesttestes unitários e integração
supertesttestar HTTP sem subir servidor
pm2process manager em produção