O que é Node.js e para que serve no back-end
Node.js é o JavaScript rodando fora do navegador: o que ele faz, onde é usado e o primeiro script executado no terminal, com a saída real.
Node.js é um programa que executa JavaScript fora do navegador, direto no seu computador ou num servidor. Com ele, a mesma linguagem que troca a cor de um botão passa a ler arquivos, falar com banco de dados e responder requisições HTTP — que é o trabalho do back-end.
Todos os exemplos desta lição são de uma livraria de bairro, a Página Sete: catálogo, estoque e o primeiro servidor que devolve os livros em JSON. Toda saída colada aqui veio de uma execução real no Node 24.16.0, num MacBook com macOS 27.
A mesma língua, outro lugar de trabalho
Uma pessoa pode falar português numa loja ou numa oficina; o idioma não muda,
mas as ferramentas ao alcance dela mudam completamente. JavaScript no navegador
tem window, DOM e eventos de tela. No Node, a mesma sintaxe encontra arquivos,
processos, rede e portas do sistema operacional.
É isso que significa chamar Node de runtime JavaScript fora do navegador.
Ele executa a linguagem com o V8 e oferece APIs próprias ao redor dela. Ao rodar
o primeiro arquivo, faça uma checagem simples: console.log funciona nos dois
ambientes, mas document só existe em um. A diferença da saída localiza com
precisão o que pertence à linguagem e o que pertence ao ambiente.
O JavaScript nasceu dentro do navegador, e por muito tempo só existiu lá. O
navegador dá à linguagem um conjunto de brinquedos: a página (document), a
janela (window), a caixinha de aviso (alert), o armazenamento local
(localStorage). Nada disso é JavaScript — é o navegador emprestando poderes.
A linguagem em si é neutra. Variável, função, array, objeto, promise: isso existe em qualquer lugar onde haja um motor de JavaScript. Se você já entendeu o que é JavaScript, já sabe boa parte do Node. O que muda não é a linguagem — é a caixa de ferramentas em volta dela.
| o que você quer fazer | no navegador | no Node |
|---|---|---|
| avisar alguma coisa | alert() |
console.log() |
| guardar um dado | localStorage |
um arquivo, ou um banco |
| buscar dado de outro sistema | fetch() |
fetch() também |
| ler um arquivo do disco | não pode | node:fs |
| escutar uma porta de rede | não pode | node:http |
| saber quem chamou o programa | não existe | process.argv |
As duas últimas linhas são o motivo de o Node existir. Um servidor precisa escutar uma porta e abrir arquivos; o navegador é proibido de fazer as duas coisas, de propósito, porque ele executa código de sites desconhecidos.
Rodando o primeiro arquivo no terminal
Sem HTML, sem <script>, sem abrir o navegador. Crie um arquivo
catalogo.js numa pasta vazia:
const livros = [
{ titulo: 'Torto Arado', autor: 'Itamar Vieira Junior', preco: 54.9, estoque: 3 },
{ titulo: 'O Avesso da Pele', autor: 'Jeferson Tenório', preco: 49.9, estoque: 0 },
{ titulo: 'A Vida Invisível', autor: 'Martha Batalha', preco: 44.9, estoque: 7 },
];
const emFalta = livros.filter((livro) => livro.estoque === 0);
console.log(`Livraria Página Sete — ${livros.length} títulos no catálogo`);
console.log(`Sem estoque: ${emFalta.map((livro) => livro.titulo).join(', ')}`);E rode passando o caminho do arquivo para o comando node:
node catalogo.jsFoi isso. O node é um executável que recebe um arquivo, lê de cima para
baixo, executa e encerra. Repare que console.log não abriu console nenhum:
ele escreveu no seu terminal, que é a saída padrão do processo.
As três peças: V8, libuv e os módulos node:
O Node não é um bloco só. Ele é uma montagem de bibliotecas, e o próprio Node sabe dizer quais versões carregou. Peça a ele:
node -p "process.versions"Essa lista é o Node desmontado em cima da mesa, e vale ler com calma:
v8é o motor que compila e executa o seu JavaScript. É o mesmo motor do Google Chrome. Quando alguém diz que “Node é rápido”, está falando do V8.uvé a libuv, biblioteca em C que conversa com o sistema operacional — disco, rede, DNS — e que implementa o event loop.undicié o cliente HTTP que faz ofetchfuncionar no servidor;opensslcuida do HTTPS;zlibebrotlicomprimem resposta;icué quem sabe formatarR$ 1.234,56em português;sqliteé um banco de dados que já vem dentro do Node desde a versão 22.
O -p é de print: ele avalia a expressão e imprime o resultado. Existe também
o -e, de execute, que roda o código e não imprime nada por conta própria.
Os módulos internos vêm com o prefixo node:. Um relatório do estoque da
livraria, lendo um estoque.json que está na mesma pasta:
const fs = require('node:fs/promises');
const path = require('node:path');
async function main() {
const arquivo = path.join(__dirname, 'estoque.json');
const livros = JSON.parse(await fs.readFile(arquivo, 'utf8'));
const parado = livros.reduce((soma, l) => soma + l.preco * l.estoque, 0);
console.log('Arquivo lido:', arquivo);
console.log('Títulos:', livros.length);
console.log('Parado em estoque: R$', parado.toFixed(2));
}
main();require é a forma antiga de importar, do sistema CommonJS, e é o que o Node
usa quando não existe um package.json dizendo o contrário. A forma moderna é
import, e a diferença entre as duas tem
uma lição só para ela mais para a frente.
O REPL: testar uma ideia sem criar arquivo
Digite node sozinho, sem nome de arquivo, e você cai num console interativo —
o REPL (read, eval, print, loop). É onde eu confiro uma dúvida de dois
segundos sem poluir o projeto com um teste.js:
nodeDuas coisas para reparar. A declaração const preco = 54.9 devolveu
undefined, porque declarar não é um valor — o REPL imprime o resultado da
expressão, e uma declaração não tem resultado. E o Intl formatou em real
brasileiro sem instalar nada: é o icu da lista de versões trabalhando.
O que o Node não tem: window, document e localStorage
Essa é a parte que confunde quem vem do front-end. Vamos perguntar ao próprio Node quais globais existem:
const globais = ['window', 'document', 'alert', 'localStorage', 'process'];
for (const nome of globais) {
console.log(nome.padEnd(13), typeof globalThis[nome]);
}Quatro undefined e um object. Agora a mesma lista, feita ao ambiente de
navegador. Não dá para colar aqui o console do Chrome, então usei o jsdom —
uma implementação do DOM escrita em JavaScript, a mesma que testes de front-end
usam para simular uma página:
import { JSDOM } from 'jsdom';
const { window } = new JSDOM('<!doctype html><p>Livraria Página Sete</p>', {
url: 'https://livraria.exemplo/catalogo',
});
const globais = ['window', 'document', 'alert', 'localStorage', 'process'];
for (const nome of globais) {
console.log(nome.padEnd(13), typeof window[nome]);
}As duas tabelas são a imagem espelhada uma da outra. Onde o navegador tem
página, o Node tem processo. O process é o objeto que descreve o programa em
execução: os argumentos da linha de comando, as variáveis de ambiente, o código
de saída. O DOM, do outro lado, simplesmente não
existe no servidor — não há página para manipular.
fetch é a exceção simpática: existe nos dois desde o Node 18, com a mesma
assinatura. O jsdom não implementa fetch, mas o navegador de verdade sim.
O erro que aparece na primeira semana
Quem chega do front-end tenta rodar no Node um código que só faz sentido na
página. É sempre o mesmo arquivo: uma vitrine que monta <li> no DOM.
const livros = require('./estoque.json');
const lista = document.querySelector('#vitrine');
for (const livro of livros) {
const item = document.createElement('li');
item.textContent = `${livro.titulo} — R$ ${livro.preco.toFixed(2)}`;
lista.appendChild(item);
}ReferenceError: document is not defined at Object.<anonymous> (/private/tmp/pagina-sete/vitrine.js:3:15) at Module._compile (node:internal/modules/cjs/loader:1854:14) at Object..js (node:internal/modules/cjs/loader:1985:10) at Module.load (node:internal/modules/cjs/loader:1577:32) at Module._load (node:internal/modules/cjs/loader:1379:12) at wrapModuleLoad (node:internal/modules/cjs/loader:255:19) at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)
Node.js v24.16.0
Repare que a linha 1 funcionou: o require do JSON passou sem reclamar. O
problema não é o arquivo inteiro estar errado, é uma peça de navegador no lugar
errado. ReferenceError: document is not defined quer dizer, ao pé da letra,
“esse nome não existe neste ambiente”.
A correção nunca é instalar um pacote que finge ser navegador. É separar os papéis: o Node calcula e entrega os dados, o navegador desenha. Daqui a algumas lições, a livraria vai ter o back-end mandando JSON e o front-end montando a lista — cada um no seu ambiente.
Uma thread só não quer dizer devagar
Você vai ouvir que o Node é single-threaded. É verdade para o seu JavaScript: existe uma thread só executando o seu código. E, ainda assim, três consultas lentas terminam juntas.
const inicio = performance.now();
function consultar(fornecedor, ms) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`${fornecedor} respondeu em ${(performance.now() - inicio).toFixed(0)}ms`);
resolve();
}, ms);
});
}
async function main() {
await Promise.all([
consultar('Distribuidora A', 300),
consultar('Distribuidora B', 300),
consultar('Distribuidora C', 300),
]);
console.log(`total: ${(performance.now() - inicio).toFixed(0)}ms`);
}
main();Três esperas de 300ms somaram 304ms, não 900ms. Em cinco execuções seguidas o total ficou entre 301ms e 304ms. A espera não gasta thread: quem espera é o sistema operacional, via libuv, e o seu JavaScript fica livre nesse meio-tempo.
O reverso também é fácil de provar. Se o trabalho for cálculo puro, ele segura a thread e todo o resto fica na fila:
const inicio = performance.now();
setTimeout(() => {
console.log(`atendimento chegou em ${(performance.now() - inicio).toFixed(0)}ms`);
}, 0);
let paginas = 0;
for (let i = 0; i < 3_000_000_000; i++) paginas += 1;
console.log(`contagem terminou em ${(performance.now() - inicio).toFixed(0)}ms`);O setTimeout foi agendado para 0ms e só rodou depois de 1,5 segundo. Em cinco
execuções o laço levou de 1274ms a 1589ms, e o timer sempre chegou 2ms depois
dele. Num servidor de verdade, esse laço é o cliente que fica sem resposta.
Resumindo a regra: espera é de graça, cálculo é caro. O mecanismo que organiza isso é o event loop do Node.
Do script ao servidor: quinze linhas que respondem HTTP
Até aqui o Node rodou e encerrou. Um servidor faz o contrário: ele fica de pé,
esperando. O módulo node:http já traz tudo, sem instalar nada.
const http = require('node:http');
const fs = require('node:fs/promises');
const path = require('node:path');
const servidor = http.createServer(async (req, res) => {
if (req.url === '/livros') {
const bruto = await fs.readFile(path.join(__dirname, 'estoque.json'), 'utf8');
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
res.end(bruto);
return;
}
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Página Sete: rota não encontrada');
});
servidor.listen(3737, () => console.log('Catálogo no ar em http://localhost:3737/livros'));Com o servidor rodando num terminal, bati nele com curl -i, que mostra os
cabeçalhos junto com o corpo:
curl -i http://localhost:3737/livros[ { “titulo”: “Torto Arado”, “preco”: 54.9, “estoque”: 3 }, { “titulo”: “O Avesso da Pele”, “preco”: 49.9, “estoque”: 0 }, { “titulo”: “A Vida Invisível”, “preco”: 44.9, “estoque”: 7 } ]
E numa rota que não existe, o mesmo servidor devolve 404 com texto puro:
curl -i http://localhost:3737/promocaoPágina Sete: rota não encontrada
Isso já é uma API. Feia, sem organização de rotas, sem validação — mas de pé, e qualquer front-end do mundo consegue consumir. É o mesmo caminho que a lição do servidor HTTP puro percorre com calma.
Onde o Node é usado de verdade
O Node aparece em quatro lugares distintos, e vale saber diferenciá-los porque as vagas usam palavras diferentes para cada um:
- API de back-end. É o uso mais comum e o que gera vaga com o nome “Node”.
Quase sempre com o Express, que é uma camada fina em cima do
node:httppara organizar rotas e middlewares. iFood, Nubank e boa parte das startups brasileiras têm serviços em Node nessa função. - Ferramenta de front-end. Vite, ESLint, Prettier, o próprio npm: tudo isso
é programa Node. Quem só faz interface também precisa do Node instalado —
ele é o motor por baixo do
npm run dev. - Script de automação. Renomear mil arquivos, gerar relatório, importar planilha da livraria para o banco. É onde o Node substitui o shell script sem você precisar aprender outra linguagem.
- Tempo real. Chat, notificação, dashboard que atualiza sozinho. O modelo de uma thread com muita espera é exatamente o perfil de milhares de conexões abertas fazendo pouca conta.
Para quem está começando no Brasil, o item que paga a conta é o primeiro. As vagas de júnior costumam pedir “Node + Express + Postgres ou Mongo”, e é essa combinação que a trilha de Node monta do começo ao fim.
Node, Deno e Bun: onde o Node ainda é a escolha segura
Existem dois concorrentes diretos, criados para consertar decisões antigas do Node. Os dois são bons. Nenhum dos dois é onde você deve começar hoje.
| Node | Deno | Bun | |
|---|---|---|---|
| idade | 2009 | 2020 | 2022 |
| motor | V8 | V8 | JavaScriptCore |
| TypeScript direto | não, precisa compilar | sim | sim |
| vagas no Brasil | milhares | dezenas | dezenas |
| material em português | enorme | pouco | pouco |
Minha posição, e ela é uma escolha, não uma verdade: aprenda Node primeiro. Não porque seja tecnicamente superior — em vários pontos não é — mas porque o seu objetivo agora não é ter a ferramenta mais elegante, é conseguir a primeira vaga e ter onde procurar resposta quando travar às onze da noite. Quando o Node já for natural, migrar para Bun leva uma tarde: a linguagem é a mesma e a maior parte da API também.
O que instalar antes da próxima lição
Confira o que já existe na sua máquina:
node --version
npm --versionSe os dois comandos responderem com um número, você está pronto. Se algum disser “command not found”, ou se a versão do Node for menor que 20, o próximo passo é instalar o Node com o nvm — o gerenciador que deixa cada projeto na sua própria versão, sem quebrar os outros. Instalar pelo site oficial funciona, mas você vai se arrepender no dia em que um projeto antigo pedir Node 18.
Depois disso, a livraria Página Sete ganha package.json, dependências e
rotas de verdade. O guia de Node mostra a ordem inteira,
desta lição até o deploy da API.
Prefere aprender em vídeo?
Tem uma aula sobre este assunto no nosso canal.
Perguntas frequentes
Node.js é uma linguagem de programação?
Preciso saber JavaScript antes de aprender Node?
Node serve só para back-end?
Qual versão do Node devo instalar?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- Node.js — About — nodejs.org
- Node.js Docs — process.versions — nodejs.org
- libuv — Design overview — docs.libuv.org



