Javascript

Introdução ao Node.js: JavaScript fora do navegador Já leu

12 min de leitura

Introdução ao Node.js: JavaScript fora do navegador
Até agora todo o JavaScript que escrevemos rodou no navegador. O navegador é um ambiente rico — tem o DOM, eventos, LocalStorage, APIs de geolocalização. Mas ele tem uma limitação fundamental: só existe dentro de uma aba

Até agora todo o JavaScript que escrevemos rodou no navegador. O navegador é um ambiente rico — tem o DOM, eventos, LocalStorage, APIs de geolocalização. Mas ele tem uma limitação fundamental: só existe dentro de uma aba.

O Node.js quebrou essa barreira em 2009. Pela primeira vez, JavaScript podia rodar no servidor — lendo arquivos do disco, abrindo conexões de rede, criando APIs, acessando bancos de dados. A mesma linguagem que você usa no front-end, agora também no back-end.

Este artigo apresenta o Node.js do zero — o que é, como funciona, o que muda em relação ao navegador e como dar os primeiros passos.


O que é Node.js?

Node.js é um ambiente de execução JavaScript construído sobre o motor V8 do Chrome. Não é uma linguagem nova — é JavaScript. O que muda é o ambiente onde ele roda e as APIs disponíveis.

┌──────────────────────────────────────────────────────┐
│                    COMPARATIVO                       │
│                                                      │
│  NAVEGADOR                    NODE.JS                │
│  ─────────────────────────    ──────────────────     │
│  Motor: V8 (Chrome)           Motor: V8              │
│  DOM, window, document        Sem DOM                │
│  fetch, localStorage          Módulos nativos        │
│  alert, confirm               fs, path, os, http     │
│  Limitado ao sandbox          Acesso total ao OS     │
│  Roda na aba do usuário       Roda no servidor       │
│  Distribuído ao cliente       Fica no servidor       │
└──────────────────────────────────────────────────────┘

Instalando e verificando o Node.js

# Verificar se já está instalado
node --version    # ex: v20.11.0
npm --version     # ex: 10.2.4

# Instalar (se necessário): https://nodejs.org
# Recomendado: versão LTS (Long Term Support)

Seu primeiro programa Node.js

Crie um arquivo ola.js:

// ola.js
console.log("Olá, Node.js!");
console.log("Versão:", process.version);
console.log("Sistema:", process.platform);
console.log("Diretório atual:", process.cwd());

Execute no terminal:

node ola.js
# Olá, Node.js!
# Versão: v20.11.0
# Sistema: linux
# Diretório atual: /home/usuario/projetos

Sem navegador. Sem HTML. Só JavaScript rodando direto no sistema operacional.


O objeto process — a ponte com o sistema

No navegador temos window. No Node, temos process:

// Informações do ambiente
console.log(process.version);      // versão do Node
console.log(process.platform);     // "linux", "win32", "darwin"
console.log(process.arch);         // "x64", "arm64"
console.log(process.cwd());        // diretório de trabalho atual
console.log(process.env.HOME);     // variáveis de ambiente
console.log(process.pid);          // ID do processo

// Argumentos passados na linha de comando
// node script.js arg1 arg2
console.log(process.argv);
// ["node", "/path/script.js", "arg1", "arg2"]

const args = process.argv.slice(2); // remove "node" e o nome do arquivo
console.log(args); // ["arg1", "arg2"]

// Encerrar o processo
process.exit(0);  // 0 = sucesso
process.exit(1);  // 1 = erro

Sistema de módulos — CommonJS vs ES Modules

Node.js suporta dois sistemas de módulos. Entender a diferença é fundamental:

// ── CommonJS (padrão histórico do Node) ──────────
// Arquivos .js usam require() e module.exports

// matematica.js
function somar(a, b) { return a + b; }
function multiplicar(a, b) { return a * b; }

module.exports = { somar, multiplicar };
// ou exportar um por um:
module.exports.somar = somar;

// app.js
const { somar, multiplicar } = require("./matematica");
console.log(somar(3, 4));        // 7
console.log(multiplicar(3, 4));  // 12

// Módulos nativos do Node
const fs = require("fs");
const path = require("path");
const os = require("os");

// ── ES Modules (moderno, recomendado) ────────────
// Arquivos .mjs OU "type": "module" no package.json

// matematica.mjs
export function somar(a, b) { return a + b; }
export function multiplicar(a, b) { return a * b; }
export default { somar, multiplicar };

// app.mjs
import { somar, multiplicar } from "./matematica.mjs";
import fs from "fs";

Para novos projetos, prefira ES Modules. Para código legado e muitas bibliotecas, você ainda encontrará CommonJS.


Módulo fs — trabalhando com arquivos

O módulo fs (file system) é um dos mais usados no Node:

const fs = require("fs");
const path = require("path");

// ── Leitura ─────────────────────────────────────

// Síncrono — bloqueia a thread (evitar em servidores)
const conteudo = fs.readFileSync("arquivo.txt", "utf-8");
console.log(conteudo);

// Assíncrono com callback
fs.readFile("arquivo.txt", "utf-8", (erro, conteudo) => {
  if (erro) {
    console.error("Erro ao ler:", erro.message);
    return;
  }
  console.log(conteudo);
});

// Assíncrono com Promise (recomendado)
const fsPromises = require("fs").promises;
// ou: const { readFile } = require("fs/promises");

async function lerArquivo(caminho) {
  try {
    const conteudo = await fsPromises.readFile(caminho, "utf-8");
    return conteudo;
  } catch (erro) {
    if (erro.code === "ENOENT") {
      throw new Error(`Arquivo não encontrado: ${caminho}`);
    }
    throw erro;
  }
}

// ── Escrita ─────────────────────────────────────

// Sobrescreve o arquivo (cria se não existir)
await fsPromises.writeFile("saida.txt", "Olá, arquivo!", "utf-8");

// Adiciona ao final sem apagar o conteúdo existente
await fsPromises.appendFile("log.txt", `${new Date().toISOString()} — evento
`);

// ── Operações de diretório ───────────────────────

// Criar diretório (recursive: cria diretórios pais também)
await fsPromises.mkdir("pasta/subpasta", { recursive: true });

// Listar arquivos
const arquivos = await fsPromises.readdir("./");
console.log(arquivos);

// Verificar se existe
try {
  await fsPromises.access("arquivo.txt");
  console.log("Existe!");
} catch {
  console.log("Não existe.");
}

// Informações do arquivo
const stats = await fsPromises.stat("arquivo.txt");
console.log(`Tamanho: ${stats.size} bytes`);
console.log(`Modificado: ${stats.mtime}`);
console.log(`É diretório: ${stats.isDirectory()}`);

// Renomear / mover
await fsPromises.rename("antigo.txt", "novo.txt");

// Deletar
await fsPromises.unlink("arquivo.txt");
await fsPromises.rm("pasta", { recursive: true }); // pasta com conteúdo

Módulo path — trabalhando com caminhos

Caminhos de arquivo são diferentes em Windows () e Unix (/). O módulo path resolve isso:

const path = require("path");

// Construir caminhos de forma segura (multiplataforma)
const caminho = path.join("/usuarios", "ana", "documentos", "arquivo.txt");
// "/usuarios/ana/documentos/arquivo.txt" no Unix
// "\usuarios\ana\documentos\arquivo.txt" no Windows

// Caminho absoluto a partir de caminho relativo
const absoluto = path.resolve("pasta", "arquivo.txt");
// "/projeto/atual/pasta/arquivo.txt"

// Partes do caminho
console.log(path.dirname("/pasta/subpasta/arquivo.txt")); // "/pasta/subpasta"
console.log(path.basename("/pasta/arquivo.txt"));          // "arquivo.txt"
console.log(path.basename("/pasta/arquivo.txt", ".txt")); // "arquivo"
console.log(path.extname("/pasta/arquivo.txt"));           // ".txt"

// __dirname e __filename (CommonJS) — diretório e arquivo atual
console.log(__dirname);  // "/home/usuario/projeto"
console.log(__filename); // "/home/usuario/projeto/app.js"

// Equivalente em ES Modules
import { fileURLToPath } from "url";
import { dirname } from "path";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

Módulo os — informações do sistema operacional

const os = require("os");

console.log(os.platform());    // "linux", "darwin", "win32"
console.log(os.arch());        // "x64", "arm64"
console.log(os.hostname());    // nome do computador
console.log(os.homedir());     // "/home/usuario"
console.log(os.tmpdir());      // "/tmp"
console.log(os.cpus().length); // número de CPUs

// Memória
const totalMem = os.totalmem();
const livMem = os.freemem();
console.log(`Memória total: ${(totalMem / 1024 / 1024 / 1024).toFixed(1)} GB`);
console.log(`Memória livre: ${(livMem / 1024 / 1024 / 1024).toFixed(1)} GB`);
console.log(`Uso: ${Math.round((1 - livMem / totalMem) * 100)}%`);

// Uptime
console.log(`Sistema rodando há ${Math.floor(os.uptime() / 3600)} horas`);

Um script prático — processador de arquivos

Vamos construir um script real que lê um arquivo CSV, processa os dados e gera um relatório:

// relatorio.js
const fs = require("fs/promises");
const path = require("path");

// Dados de exemplo — normalmente lidos de um arquivo real
const csvExemplo = `nome,departamento,salario,ativo
Ana Paula,Engenharia,8500,true
Carlos Silva,Design,6200,true
Beatriz Costa,Engenharia,9100,false
Diego Mendes,Marketing,5800,true
Elena Souza,Engenharia,7700,true
Fernando Lima,Design,6800,false
Gabriela Ramos,Marketing,5500,true
Hugo Santos,Engenharia,10200,true`;

function parsearCSV(texto) {
  const linhas = texto.trim().split("
");
  const cabecalho = linhas[0].split(",");

  return linhas.slice(1).map(linha => {
    const valores = linha.split(",");
    return cabecalho.reduce((obj, col, i) => {
      const val = valores[i]?.trim();
      // Conversão de tipos
      if (val === "true") obj[col] = true;
      else if (val === "false") obj[col] = false;
      else if (!isNaN(val) && val !== "") obj[col] = Number(val);
      else obj[col] = val;
      return obj;
    }, {});
  });
}

function gerarRelatorio(funcionarios) {
  const ativos = funcionarios.filter(f => f.ativo);
  const inativos = funcionarios.filter(f => !f.ativo);

  // Agrupamento por departamento
  const porDepartamento = funcionarios.reduce((acc, f) => {
    if (!acc[f.departamento]) {
      acc[f.departamento] = { funcionarios: [], totalSalario: 0 };
    }
    acc[f.departamento].funcionarios.push(f);
    acc[f.departamento].totalSalario += f.salario;
    return acc;
  }, {});

  const totalSalarios = ativos.reduce((acc, f) => acc + f.salario, 0);
  const mediaSalarial = totalSalarios / ativos.length;
  const maiorSalario = Math.max(...funcionarios.map(f => f.salario));
  const menorSalario = Math.min(...funcionarios.map(f => f.salario));

  const separador = "═".repeat(50);

  let relatorio = `
${separador}
   RELATÓRIO DE RECURSOS HUMANOS
   Gerado em: ${new Date().toLocaleString("pt-BR")}
${separador}

📊 RESUMO GERAL
${"─".repeat(50)}
  Total de funcionários : ${funcionarios.length}
  Funcionários ativos   : ${ativos.length}
  Funcionários inativos : ${inativos.length}
  Folha de pagamento    : R$ ${totalSalarios.toLocaleString("pt-BR", { minimumFractionDigits: 2 })}
  Salário médio         : R$ ${mediaSalarial.toLocaleString("pt-BR", { minimumFractionDigits: 2 })}
  Maior salário         : R$ ${maiorSalario.toLocaleString("pt-BR", { minimumFractionDigits: 2 })}
  Menor salário         : R$ ${menorSalario.toLocaleString("pt-BR", { minimumFractionDigits: 2 })}

🏢 POR DEPARTAMENTO
${"─".repeat(50)}`;

  Object.entries(porDepartamento)
    .sort((a, b) => b[1].totalSalario - a[1].totalSalario)
    .forEach(([depto, dados]) => {
      const media = dados.totalSalario / dados.funcionarios.length;
      relatorio += `
  ${depto}
    Funcionários : ${dados.funcionarios.length}
    Total folha  : R$ ${dados.totalSalario.toLocaleString("pt-BR", { minimumFractionDigits: 2 })}
    Média salarial: R$ ${media.toLocaleString("pt-BR", { minimumFractionDigits: 2 })}
    Membros: ${dados.funcionarios.map(f => f.nome.split(" ")[0]).join(", ")}
`;
    });

  relatorio += `
👥 LISTA DE FUNCIONÁRIOS
${"─".repeat(50)}
${"Nome".padEnd(20)} ${"Depto".padEnd(12)} ${"Salário".padEnd(12)} Status`;
  relatorio += "
" + "─".repeat(50);

  funcionarios
    .sort((a, b) => b.salario - a.salario)
    .forEach(f => {
      const status = f.ativo ? "✅ Ativo" : "❌ Inativo";
      const salario = `R$ ${f.salario.toLocaleString("pt-BR")}`;
      relatorio += `
${f.nome.padEnd(20)} ${f.departamento.padEnd(12)} ${salario.padEnd(12)} ${status}`;
    });

  relatorio += `

${separador}
`;
  return relatorio;
}

async function main() {
  // Argumentos da linha de comando
  const args = process.argv.slice(2);
  const arquivoEntrada = args[0] || null;
  const arquivoSaida = args[1] || "relatorio.txt";

  let csvTexto;

  if (arquivoEntrada) {
    console.log(`📂 Lendo arquivo: ${arquivoEntrada}`);
    csvTexto = await fs.readFile(arquivoEntrada, "utf-8");
  } else {
    console.log("📝 Usando dados de exemplo...");
    csvTexto = csvExemplo;
  }

  const funcionarios = parsearCSV(csvTexto);
  console.log(`✅ ${funcionarios.length} funcionários carregados.`);

  const relatorio = gerarRelatorio(funcionarios);

  // Exibe no console
  console.log(relatorio);

  // Salva em arquivo
  const caminhoSaida = path.resolve(arquivoSaida);
  await fs.writeFile(caminhoSaida, relatorio, "utf-8");
  console.log(`💾 Relatório salvo em: ${caminhoSaida}`);
}

main().catch(erro => {
  console.error("❌ Erro fatal:", erro.message);
  process.exit(1);
});

Execute:

node relatorio.js
# ou com arquivo real:
node relatorio.js funcionarios.csv relatorio.txt

REPL — o terminal interativo do Node

# Inicia o REPL (Read-Eval-Print Loop)
node

# Agora você pode digitar JavaScript diretamente:
> 2 + 2
4
> const nome = "Ana"
undefined
> `Olá, ${nome}!`
'Olá, Ana!'
> [1,2,3].map(n => n * 2)
[ 2, 4, 6 ]
> .exit   # ou Ctrl+D para sair

Ótimo para testar código rapidamente sem criar arquivos.


O que Node.js tem que o navegador não tem

// Acesso ao sistema de arquivos
const fs = require("fs/promises");
await fs.readFile("/etc/hostname");

// Variáveis de ambiente (segredos, configurações)
const dbUrl = process.env.DATABASE_URL;
const porta = process.env.PORT || 3000;

// Informações do processo
process.pid;           // ID do processo
process.memoryUsage(); // uso de memória
process.uptime();      // tempo rodando

// Criação de processos filhos
const { exec } = require("child_process");
exec("ls -la", (erro, stdout) => console.log(stdout));

// Servidor TCP/HTTP nativo
const http = require("http");
const servidor = http.createServer((req, res) => {
  res.end("Olá!");
});
servidor.listen(3000);

// Streams — processamento de dados em fluxo
const readable = fs.createReadStream("arquivo-grande.txt");
readable.on("data", chunk => processar(chunk));

O que o navegador tem que Node não tem

// ❌ Não existe no Node.js:
document.querySelector("#btn");
window.localStorage;
window.alert("mensagem");
fetch("url"); // existe, mas só no Node 18+
navigator.geolocation;

Boas práticas ao começar com Node.js

# ✅ 1. Sempre crie um package.json no início do projeto
npm init -y

# ✅ 2. Use .env para variáveis sensíveis
# Crie um arquivo .env
# PORT=3000
# DATABASE_URL=postgres://...

# ✅ 3. Nunca commite .env no Git
# Adicione ao .gitignore:
echo ".env" >> .gitignore
echo "node_modules" >> .gitignore

# ✅ 4. Use versão LTS do Node
node --version  # deve começar com número par: v20, v22...
// ✅ 5. Trate erros de processo
process.on("uncaughtException", (erro) => {
  console.error("Erro não capturado:", erro);
  process.exit(1);
});

process.on("unhandledRejection", (erro) => {
  console.error("Promise rejeitada não tratada:", erro);
  process.exit(1);
});

// ✅ 6. Prefira fs/promises em vez de fs com callbacks
// ❌ Evitar
fs.readFile("arquivo.txt", "utf-8", callback);

// ✅ Prefira
const conteudo = await fsPromises.readFile("arquivo.txt", "utf-8");

// ✅ 7. Use path.join para construir caminhos
// ❌ Evitar
const caminho = __dirname + "/pasta/" + arquivo;

// ✅ Prefira
const caminho = path.join(__dirname, "pasta", arquivo);

Tarefa para você

Construa um script de backup em Node.js que:

1. Lê todos os arquivos .txt e .json de um diretório
2. Cria um diretório "backup/YYYY-MM-DD" com a data atual
3. Copia todos os arquivos para o backup
4. Gera um arquivo "manifesto.json" no backup com:
   - nome de cada arquivo
   - tamanho em bytes
   - data de modificação
   - hash MD5 do conteúdo (use o módulo crypto nativo)
5. Exibe um resumo no terminal:
   - Quantos arquivos copiados
   - Tamanho total do backup
   - Caminho do diretório de backup

// Dica — módulo crypto para MD5:
const crypto = require("crypto");
const hash = crypto.createHash("md5").update(conteudo).digest("hex");

Conclusão

Neste artigo você aprendeu:

  • O que é o Node.js e como ele difere do navegador
  • O objeto process e seu papel no ambiente Node
  • CommonJS vs ES Modules — os dois sistemas de módulos
  • O módulo fs para leitura, escrita e manipulação de arquivos
  • O módulo path para trabalhar com caminhos de forma multiplataforma
  • O módulo os para informações do sistema operacional
  • Como construir scripts práticos com argumentos e saída em arquivo
  • O REPL para testes rápidos no terminal
  • As diferenças entre o ambiente Node e o navegador
  • Boas práticas desde o início

 

📚 Fontes e Referências

  • Node.js Official Docs: https://nodejs.org/en/docs
  • Node.js — fs module: https://nodejs.org/api/fs.html
  • Node.js — path module: https://nodejs.org/api/path.html
  • Node.js — os module: https://nodejs.org/api/os.html
  • Node.js — process object: https://nodejs.org/api/process.html
  • JavaScript.info — Node.js: https://javascript.info/nodejs
  • Node.js Design Patterns — Mario Casciaro (Packt Publishing)
  • Learning Node.js Development — Andrew Mead (Packt Publishing)
  • roadmap.sh — Node.js Roadmap: https://roadmap.sh/nodejs
Comentários

Mais em Javascript

Eventos: click, input, submit e muito mais
Eventos: click, input, submit e muito mais

No artigo anterior aprendemos a selecionar e modificar elementos do DOM. Mas...

Async/Await: escrevendo código assíncrono de forma limpa
Async/Await: escrevendo código assíncrono de forma limpa

As Promises resolveram o Callback Hell. Mas encadear muitos .then() aind...

React Hooks em profundidade
React Hooks em profundidade

No artigo anterior você aprendeu useState e useEffect — os dois hooks mais us...