Guia completo
Node.js: guia de back-end, APIs e roteiro de estudo
Use este mapa para aprender runtime, npm, módulos, HTTP, Express, testes e deploy em Node.js antes de construir uma API completa.
Node.js é o JavaScript rodando fora do navegador. Com ele, a mesma linguagem que você usa para mexer numa página passa a atender requisições, ler arquivo e falar com banco de dados. A primeira rota que devolve JSON cabe em cinco linhas.
Este guia é o mapa do caminho inteiro: o que dá para construir, em que ordem estudar, qual das três formas de subir um servidor escolher — com as três medidas na mesma máquina — e onde as pessoas travam de verdade. O exemplo que atravessa tudo é a API de uma livraria de bairro, a Página Sete, a mesma que aparece nas lições da trilha de Node.
Um pré-requisito honesto antes de começar: você precisa de JavaScript. Variável, função, array, objeto e promise. Se algum desses nomes ainda é vago, comece pelo guia de JavaScript e volte aqui — Node não acrescenta sintaxe nenhuma à linguagem, só acrescenta o mundo em volta dela.
Uma oficina fora do navegador: o mapa mental do Node
No navegador, o JavaScript trabalha dentro de uma loja já montada: há janela,
documento, botões e cliques. O Node leva a mesma pessoa para a oficina dos
fundos. Lá não existe document, mas existem arquivo, rede, processo, porta e
variável de ambiente. A linguagem é a mesma; as ferramentas disponíveis ao
redor mudaram.
Tecnicamente, Node é um runtime: ele combina o motor V8 com APIs próprias e
um modelo de entrada e saída assíncrona. Faça uma verificação simples enquanto
lê o próximo exemplo: pergunte se cada operação dependeria de window ou do
sistema operacional. A resposta já mostra se aquele código pertence ao
navegador ou ao Node.
Node é bom em uma coisa muito específica: esperar. Esperar o banco responder, esperar o disco entregar o arquivo, esperar a API do gateway de pagamento voltar. Enquanto espera uma coisa, ele atende outras mil. É por isso que ele domina justamente estes quatro trabalhos:
API que fala JSON. O caso central desta trilha: receber uma requisição HTTP, consultar dados e devolver JSON com status coerente.
Ferramenta de linha de comando e automação. Ler uma planilha da livraria e jogar no banco, renomear três mil arquivos, gerar relatório todo dia às sete. Node substitui shell script com uma linguagem que você já conhece.
Servidor de tempo real. Chat, notificação, painel que atualiza sozinho. Muitas conexões abertas ao mesmo tempo, cada uma fazendo pouca coisa — o formato exato em que o Node é forte.
O ferramental do front-end. Vite, ESLint, Prettier, o próprio npm: tudo isso é Node. Mesmo quem só quer fazer interface já usa Node todo dia sem saber.
E existe o outro lado. Node é uma thread só para o seu código. Qualquer conta pesada e síncrona congela o servidor inteiro. Isso não é opinião, dá para medir. A livraria tem duas rotas, uma leve e um “relatório” que faz uma conta grande sem parar para respirar:
import express from 'express';
const app = express();
app.get('/livros', (req, res) => res.json({ total: 3 }));
app.get('/relatorio', (req, res) => {
let soma = 0;
for (let i = 0; i < 3e9; i += 1) soma += i; // conta pesada, e síncrona
res.json({ soma });
});
app.listen(3010, () => console.log('livraria no ar'));Agora o cliente: peço /livros sozinho, depois disparo o relatório e, cinquenta
milissegundos depois, peço /livros de novo.
const cronometrar = async (rota) => {
const inicio = performance.now();
await fetch(`http://127.0.0.1:3010${rota}`).then((r) => r.json());
return Math.round(performance.now() - inicio);
};
await cronometrar('/livros'); // aquecimento, para não medir a primeira conexão
console.log('/livros sozinho:', await cronometrar('/livros'), 'ms');
const relatorio = cronometrar('/relatorio');
await new Promise((r) => setTimeout(r, 50));
console.log('/livros durante o relatório:', await cronometrar('/livros'), 'ms');
console.log('/relatorio inteiro:', await relatorio, 'ms');Um milissegundo virou 1.727. A rota leve não ficou mais lenta: ela ficou na fila, esperando o relatório soltar a thread. Um único cliente pedindo o relatório derrubou a experiência de todos os outros.
A regra que sai daí: processamento pesado de CPU — transcodificar vídeo, redimensionar mil imagens, treinar modelo — não mora dentro do processo que atende requisição. Ou vai para outra linguagem, ou vai para um worker separado, ou vira uma fila. O event loop do Node explica o mecanismo por trás desse número.
Node ou a linguagem de back-end que você já conhece
Se você está escolhendo agora, use um critério concreto: a linguagem do back-end acompanha o problema, a base existente e a equipe que vai mantê-la.
Node ganha quando o mesmo time — ou a mesma pessoa — cuida da tela e do
servidor. Você escreve map e filter dos dois lados, compartilha a validação,
copia um trecho de um lado para o outro e ele funciona. Para quem está saindo do
front-end e quer entender o outro lado, é o caminho mais curto que existe.
Python costuma encaixar quando o projeto depende de bibliotecas de análise, ciência de dados ou treino de modelos. Java e C# fazem sentido quando a base e o time já usam seus ecossistemas; PHP, quando o produto vive em WordPress, Laravel ou outra base PHP. A pergunta útil não é qual linguagem “ganha”, e sim qual reduz risco no sistema que você realmente precisa entregar.
O que não é critério: velocidade bruta da linguagem. A sua primeira API vai gastar 40 ms conversando com o banco e 0,3 ms executando o seu código. Trocar de linguagem para ganhar esses 0,3 ms é otimizar a parte errada.
As sete etapas da trilha, na ordem em que doem menos
Esta é a ordem em que a trilha de Node foi escrita. Ela não é a ordem da documentação: é a ordem em que cada assunto para de doer, porque cada etapa só usa o que a anterior já resolveu. Cada uma termina com uma coisa que você consegue mostrar para alguém.
1. O terreno: o que é e onde roda
O que é Node.js separa a linguagem do ambiente — o que é JavaScript e o que é o Node por baixo. O event loop explica como uma thread só atende mil requisições, que é o modelo mental que vai explicar metade dos seus bugs depois. Instalar o Node com nvm evita o problema mais bobo da área: dois projetos que precisam de versões diferentes.
Entregável: um script que roda no seu terminal e imprime alguma coisa útil.
2. O projeto: npm, módulos e dependência
npm e package.json formam o cartório do projeto: o que
está instalado, em que versão, e quais comandos existem.
ESM ou CommonJS resolve a confusão entre import e
require, que é a origem do erro
Cannot find module na maior parte das
vezes.
Entregável: um projeto com package.json, uma dependência instalada e um
script npm run dev que funciona.
3. O sistema: arquivo e ambiente
Ler e escrever arquivo com fs/promises é
onde o Node deixa de ser “JavaScript no terminal” e vira programa de verdade. E
variáveis de ambiente são o assunto que
separa quem sabe fazer deploy de quem não sabe — porque senha em código não é
descuido, é incidente.
Entregável: um importador que lê um arquivo JSON e grava outro, com o caminho vindo de uma variável de ambiente.
4. O servidor, sem framework nenhum
Criar um servidor HTTP puro parece um desvio, e
é a etapa que mais rende: quem viu req e res crus entende o Express em vez de
decorar. No caminho aparece o
EADDRINUSE, o primeiro erro de
servidor da vida de todo mundo. Junto vêm os conceitos que o framework não
ensina: o que é uma API REST, métodos HTTP e status code.
Entregável: a rota GET /livros respondendo JSON sem nenhuma dependência
instalada.
5. Express: o framework que você vai usar
Aqui a produtividade muda de patamar.
A primeira rota no Express,
req.params, req.query, req.body, middleware e a ordem do next() — e o erro que
nasce de responder duas vezes,
Cannot set headers after they are sent.
Depois o projeto começa a crescer e precisa de organização:
express.Router para dividir por recurso, CORS para o front conseguir chamar e
tratamento de erro centralizado.
Entregável: o CRUD completo da livraria, com rotas separadas por arquivo.
6. A API que aguenta o mundo real
Uma API que só funciona com dados certos não está pronta.
Validar a entrada com Zod é o que impede que
um preco: "muito caro" chegue ao banco.
Upload de arquivo com multer coloca a
capa do livro no ar. A sequência fecha com dois mecanismos comuns de segurança:
hash de senha com bcrypt e autenticação com JWT.
Entregável: rota de cadastro, rota de login e uma rota que só responde com token válido.
7. Fechar o ciclo: teste e deploy
Testar rota com node:test e supertest mostra que
o Node moderno já traz o test runner embutido, sem Jest.
Deploy de API Node coloca a coisa numa URL
pública, com porta, variável de ambiente e healthcheck. A comparação
Axios ou fetch resolve a última pergunta: como
consumir outra API de dentro da sua.
Entregável: a API da Página Sete rodando numa URL que outra pessoa consegue abrir.
Para juntar essas sete etapas num único repositório, siga o tutorial API Node com TypeScript, Express, Prisma e Docker. Ele começa no ambiente vazio e termina com banco, testes e smoke test do sistema.
http nativo, Express ou Fastify: a tabela de decisão medida
Essa é a pergunta que mais aparece, e quase sempre é respondida com opinião. Eu escrevi a mesma API da livraria nas três formas, na mesma máquina, e medi.
O código nativo, com o módulo node:http que já vem instalado:
import { createServer } from 'node:http';
import { livros } from './livros.mjs';
createServer((req, res) => {
if (req.method === 'GET' && req.url === '/livros') {
res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });
res.end(JSON.stringify(livros));
return;
}
res.writeHead(404, { 'content-type': 'application/json; charset=utf-8' });
res.end(JSON.stringify({ erro: 'rota não encontrada' }));
}).listen(3010);O mesmo, em Express:
import express from 'express';
import { livros } from './livros.mjs';
const app = express();
app.get('/livros', (req, res) => res.json(livros));
app.listen(3010);E em Fastify:
import Fastify from 'fastify';
import { livros } from './livros.mjs';
const app = Fastify();
app.get('/livros', async () => livros);
await app.listen({ port: 3010 });Onze linhas contra cinco contra cinco. A diferença cresce quando a API cresce:
com GET /livros, GET /livros/:id, POST /livros e um 404 para rota
desconhecida, a versão nativa foi para 36 linhas, o Express para 17 e o Fastify
para 15. O que o framework vende não é velocidade — é roteamento, leitura de
corpo e status code que você não escreve à mão.
Rodei os mesmos quatro pedidos contra as três versões, uma de cada vez:
curl -s http://localhost:3010/livros/2
curl -s http://localhost:3010/livros/99
curl -s -X POST http://localhost:3010/livros \
-H 'content-type: application/json' \
-d '{"titulo":"Um Defeito de Cor","autor":"Ana Maria Gonçalves","preco":89.9}'
curl -s http://localhost:3010/catalogoNativo e Express devolveram byte por byte isso aí. O Fastify bateu nas três primeiras e discordou da última, porque tem um 404 próprio de fábrica:
Guarde esse detalhe: o corpo do erro é parte do contrato da sua API, e cada framework tem um padrão diferente. Se você não escrever o seu, quem consome vai receber três formatos distintos conforme a rota erra de um jeito ou de outro.
Agora os números. Máquina: MacBook Pro com Apple M4 Pro, 24 GB, macOS 27,
Node 24.16.0. Carga com autocannon 8.0.0, 50 conexões, 10 segundos, um
aquecimento antes; o valor é a mediana de cinco execuções. O tempo de
inicialização é a mediana de seis medições de process.uptime() no momento do
listen, descartando a primeira.
| critério | node:http puro |
Express 5.2.1 | Fastify 5.12.1 |
|---|---|---|---|
| linhas até a primeira rota JSON | 11 | 5 | 5 |
| linhas com lista, item, POST e 404 | 36 | 17 | 15 |
| pacotes instalados | 0 | 68 | 49 |
node_modules em disco |
0 | 3,8 MB | 13 MB |
npm ci com cache vazio |
— | 622 ms | 551 ms |
do node servidor.js até o listen |
17,9 ms | 34,6 ms | 53,6 ms |
| requisições por segundo | 90.464 | 64.225 | 97.737 |
Três coisas nessa tabela contrariam o que se costuma dizer por aí.
O Express tem mais pacotes e ocupa menos disco. São 68 dependências contra
49 do Fastify, e ainda assim 3,8 MB contra 13 MB. Contar pacote no npm ls não
diz nada sobre peso: dentro do Fastify vêm ajv, fast-json-stringify e um
logger completo — peças grandes, que só entram em ação quando você declara um
schema. O Express traz muitos pacotinhos; o Fastify traz poucos pacotões.
O nativo perde para o Fastify. Escrever tudo à mão não é o teto de
desempenho — o teto é o cuidado do código que você escreveu. O meu roteamento
nativo monta um new URL(req.url, ...) a cada requisição para separar o caminho.
Trocando isso por uma comparação de string direta, o mesmo servidor nativo foi
para 96.288 req/s (mediana de cinco execuções), praticamente empatando com o
Fastify. Ou seja: os 7% que faltavam não eram do node:http, eram meus.
A conta de 64 mil requisições por segundo. Esse é o pior dos três, e é mais do que a esmagadora maioria dos sistemas brasileiros vê num dia inteiro. Uma corrida completa, no terminal:
npx autocannon -c 50 -d 10 -w 1 http://127.0.0.1:3010/livros707k requests in 11.01s, 334 MB read
Repare que essa medição é do meu código, sem banco de dados. Assim que entra um
SELECT, a diferença entre os três frameworks vira ruído: o banco custa
milissegundos e eles custam microssegundos.
Com os números na mesa, uma tabela de decisão:
| o seu caso | escolha | por quê |
|---|---|---|
| entender HTTP por dentro, estudando | node:http |
ver req e res crus uma vez economiza meses de mágica |
| primeiro CRUD e projeto de portfólio | Express | rota e middleware explícitos, com poucas decisões iniciais |
| API que já existe e precisa de mais vazão na mesma máquina | Fastify | 52% mais requisições por segundo aqui, com schema de resposta junto |
| script que roda e morre: cron, importador, robô | nem um nem outro | não precisa de servidor HTTP, precisa de um arquivo .js |
| time grande, contrato de API rígido e documentado | Fastify | o schema vira validação e documentação de graça |
O critério por trás da tabela: escolha pelo tamanho da comunidade enquanto você aprende, e pelo número medido só quando tiver um número que justifique. Trocar de framework por causa de benchmark de blog, num projeto que atende duzentas pessoas por dia, é trabalho jogado fora.
As três decisões que definem a estrutura do projeto
Antes de escrever a segunda rota, três decisões economizam retrabalho. Elas não são gosto: cada uma tem um custo concreto se for tomada errada.
Primeira: ESM ou CommonJS. Escolha ESM (import/export) e declare
"type": "module" no package.json. É o padrão da linguagem, é o que o resto
do ecossistema está adotando e é o que evita o vaivém entre as duas sintaxes no
mesmo projeto. O package.json da livraria fica assim:
{
"name": "pagina-sete-api",
"version": "1.0.0",
"type": "module",
"main": "src/servidor.js",
"scripts": {
"dev": "node --watch --env-file=.env src/servidor.js",
"start": "node src/servidor.js",
"test": "node --test"
},
"dependencies": {
"express": "^5.2.1"
}
}Repare no script dev: --watch reinicia sozinho a cada salvamento e
--env-file lê o .env — os dois são do próprio Node, sem nodemon nem
dotenv. O src/servidor.js que esse script sobe é este:
import express from 'express';
const app = express();
const porta = process.env.PORT ?? 3010;
app.get('/saude', (req, res) => res.json({ ok: true, ambiente: process.env.NODE_ENV }));
app.listen(porta, (erro) => {
if (erro) throw erro;
console.log(`Página Sete no ar em http://localhost:${porta}`);
});E, no Node 24, você nem precisa do npm para rodar o script — node --run dev
basta. Num terminal sobe o servidor; no outro, o curl:
node --run devcurl -s http://localhost:3010/saudeSegunda: onde a validação mora. Na borda, antes do handler. A rota recebe
dados já confiáveis; quem duvida do formato é o middleware de validação, uma vez
só. Espalhar if (!req.body.titulo) dentro de cada rota é o caminho garantido
para uma delas esquecer.
Terceira: pasta por recurso, não por tipo. Este é o layout adotado no guia; ele mantém os arquivos de cada funcionalidade próximos:
src/
servidor.js sobe o app e nada mais
rotas.js junta os roteadores
livros/
livros.rotas.js as rotas do recurso
livros.servico.js a regra de negócio
livros.schema.js a validação da entrada
emprestimos/
...
middlewares/
erro.js o tratador centralA alternativa comum — uma pasta controllers, uma services, uma models —
funciona bem até o terceiro recurso. Depois disso, mexer no cadastro de livro
significa abrir três pastas distantes, e ninguém consegue apagar uma
funcionalidade inteira com confiança. Agrupando por recurso, a livraria e os
empréstimos vivem em caixas separadas.
O tratador de erro merece um desenho, porque a ordem importa mais do que parece. No Express, a requisição atravessa os middlewares na ordem em que foram registrados, e o tratador de erro — o único com quatro parâmetros — precisa ser o último:
Os erros da primeira semana, e o que cada um está dizendo
Boa parte do aprendizado de back-end é aprender a ler mensagem de erro. Cinco delas cobrem quase tudo no começo, e cada uma tem um diagnóstico próprio.
ReferenceError: require is not defined in ES module scope aparece no dia em
que você copia um exemplo antigo para um projeto com "type": "module":
const express = require('express');
console.log(typeof express);ReferenceError: require is not defined in ES module scope, you can use import instead at file:///private/tmp/livraria-node/requerer.mjs:1:17 at ModuleJob.run (node:internal/modules/esm/module_job:439:25) Node.js v24.16.0
O erro literalmente entrega a correção: troque por import. A diferença inteira
está no sistema de módulos escolhido pelo projeto.
ERR_MODULE_NOT_FOUND é o segundo mais comum, e diz exatamente qual pacote
falta e quem pediu por ele:
Na maior parte das vezes é npm install esquecido, ou o pacote instalado numa
pasta e o arquivo rodando em outra.
Os outros três, em resumo, cada um com o seu artigo:
EADDRINUSE: address already in use— outro processo já está na porta. Quase sempre é o seu servidor de ontem, ainda vivo.Cannot set headers after they are sent to the client— você respondeu duas vezes na mesma requisição, normalmente por falta de umreturnantes dores.json.ECONNREFUSED— você pediu para uma porta em que ninguém está ouvindo. O banco não subiu, a URL está errada, ou o servidor morreu sem você notar.
O padrão útil: erro que começa com ERR_MODULE ou Cannot find é arquivo;
erro com EADDR, ECONN ou ETIMEDOUT é rede; o resto é o seu código.
Saber em qual das três famílias você caiu já corta o problema pela metade.
Onde as pessoas travam: assíncrono, ambiente e deploy
Depois de acompanhar muita gente fazendo essa travessia, os pontos de parada se repetem. São três, e nenhum deles é sintaxe.
Assíncrono. O código deixa de acontecer de cima para baixo, e o corpo continua achando que acontece. O sintoma clássico é usar o resultado antes de ele existir:
import express from 'express';
import { readFile } from 'node:fs/promises';
const app = express();
app.get('/estoque', (req, res) => {
const dados = readFile('./estoque.json', 'utf8'); // faltou o await
res.json(JSON.parse(dados));
});
app.listen(3010, () => console.log('livraria no ar em http://localhost:3010'));"[object Promise]" is not valid JSON é uma das mensagens mais didáticas do
Node: a promessa virou texto porque você tentou tratá-la como o valor pronto.
Faltou await, e faltou async na função da rota. Se essa família de bug ainda
te pega, revise async e await antes de continuar.
Ambiente. O segundo ponto de parada é a diferença entre a sua máquina e o servidor. O exemplo mais caro é a porta:
const servidor = app.listen(process.env.PORT, () => {
console.log('porta pedida:', process.env.PORT);
console.log('porta real: ', servidor.address().port);
});Sem a variável PORT definida, o Node não dá erro: ele escolhe uma porta
livre qualquer. Na sua máquina você nem percebe; na hospedagem, o healthcheck
bate na porta esperada, não encontra ninguém, e o deploy é marcado como falho
sem nenhuma mensagem que explique. Por isso process.env.PORT ?? 3010, com
valor de reserva explícito.
Deploy. O terceiro. Dois detalhes derrubam mais gente do que qualquer configuração complicada.
O primeiro é o endereço de escuta. Ouvir em 127.0.0.1 funciona lindamente na
sua máquina e some dentro de um contêiner:
const host = process.env.HOST ?? '127.0.0.1';
app.listen(3010, host, () => console.log('ouvindo em', host, 'porta 3010'));Com host em 127.0.0.1, pedi a mesma rota pelo endereço da máquina na rede:
curl -sS http://192.168.68.106:3010/saudeO mesmo servidor, ouvindo em 0.0.0.0, respondeu {"ok":true} na mesma URL.
127.0.0.1 aceita só quem vem de dentro da mesma máquina — e, num contêiner, o
resto do mundo está sempre fora.
O segundo é mais traiçoeiro, e vale conhecer: no Express 5, a função que você
passa para o listen também é o tratador de erro dele. O código-fonte
registra o último argumento em server.once('error', done). Ou seja:
app.listen(3010, (erro) => {
if (erro) {
console.error('não subiu:', erro.code);
process.exit(1);
}
console.log('livraria no ar em http://localhost:3010');
});Sem esse if (erro), o mesmo programa imprime “livraria no ar em
http://localhost:3010” com a porta ocupada e o servidor não no ar — foi
exatamente o que aconteceu aqui quando escrevi o exemplo. O servidor HTTP puro
não faz isso: ele estoura com EADDRINUSE e você fica sabendo. Se a sua mensagem
de sucesso aparece e a API não responde, é aqui que você olha.
O que você não precisa aprender agora
Metade da ansiedade de quem entra no back-end vem de uma lista de tecnologias que alguém jurou ser obrigatória. Adiar as coisas certas é o que mantém o estudo possível.
TypeScript. Vale muito, e vale depois de duas ou três APIs escritas em JavaScript puro. Tipos resolvem um problema de projeto grande; antes de sentir esse problema, eles só dobram a quantidade de erro que você precisa entender.
Docker e Kubernetes. Docker aparece naturalmente no deploy, e um
Dockerfile de dez linhas resolve. Kubernetes é ferramenta de time de
infraestrutura, não de quem está fazendo a primeira API.
Microserviços. A decisão de quebrar um sistema em vários serviços resolve um problema organizacional de empresa com muitos times. Num projeto de uma pessoa, ela só multiplica o número de coisas que podem quebrar.
GraphQL, gRPC, WebSocket. Cada um resolve um caso específico. Comece pelo contrato HTTP e REST desta trilha; depois compare quando requisitos como assinatura em tempo real, streaming ou consultas flexíveis aparecerem.
ORM antes de SQL. Prisma e Mongoose são ótimos e escondem exatamente o que
você precisa aprender. Escreva SELECT e JOIN na mão primeiro; o ORM depois
vira conforto em vez de mágica.
O que vale antecipar é Git, mesmo básico. Não pela entrevista: porque é o que transforma “consegui” em “consigo voltar quando quebrar”.
Como saber que você já sabe Node o suficiente
Não existe prova, mas existe um teste honesto. Se você consegue fazer estas sete coisas sem consultar tutorial, está pronto para se candidatar:
- Subir um projeto do zero:
npm init,"type": "module", scriptdevcom--watch, e uma rota respondendo JSON. - Explicar a diferença entre
200,201,400,401,404e500— e escolher o certo sem pensar muito. - Ler o corpo de um
POST, validar os campos e recusar o que estiver errado com uma mensagem que ajuda quem chamou. - Dividir a API em arquivos por recurso, com um roteador por recurso e um tratador de erro só.
- Guardar segredo em variável de ambiente e explicar por que ele não pode ir para o repositório.
- Ler um stack trace inteiro e apontar a linha do seu arquivo no meio das
linhas de
node_modules. - Colocar a API numa URL pública e mandar o link para outra pessoa abrir.
Se travou em algum item, ele é a sua próxima etapa — e cada um deles tem uma lição na trilha.
O que estudar depois: banco, filas e TypeScript
Com a API de pé, três direções fazem sentido, nesta ordem.
Banco de dados. É o próximo assunto obrigatório, e é maior que Node. Modelar tabela, escrever consulta, entender índice e transação. A trilha de banco de dados mostra o caminho do SQL até o Prisma, e é ele que transforma o array em memória da livraria em dado que sobrevive a um reinício.
Filas e trabalho em segundo plano. Lembra do relatório que congelou o
servidor por 1,7 segundo? A resposta profissional é tirar esse trabalho da
requisição: a rota registra o pedido, devolve 202 e um processo separado
executa. É o assunto que separa API de brinquedo de API de produção.
TypeScript. Agora sim. Depois de duas APIs em JavaScript, os tipos deixam de
ser burocracia e viram o que evita o undefined chegar no banco. O mapa da
trilha de TypeScript segue a mesma lógica.
Para começar hoje, na ordem certa, entre pela trilha de Node do zero — cada lição tem código executado, a saída real e o erro mais comum daquele assunto. Se você quer só a primeira vitória rápida, pule direto para a primeira rota no Express e volte para o começo depois: ver a coisa respondendo JSON é o que sustenta as outras seis etapas.
Trilha
Node e APIs
JavaScript no servidor: rotas, banco de dados, autenticação e a API que o front consome.
- 01O que é Node.js e para que serve no back-end
- 02Instalar o Node.js com nvm e trocar de versão por projeto
- 03npm e package.json: instalar pacote, semver e scripts
- 04ESM ou CommonJS no Node: import, require e o campo type
- 05Ler e escrever arquivo no Node com fs/promises e path
- 06Variáveis de ambiente no Node: process.env e o arquivo .env
- 07Criar um servidor HTTP no Node sem framework nenhum
- 08Deploy de API Node: porta, env, healthcheck e Dockerfile
api node
API Node com TypeScript, Express, Prisma e Docker
node
Axios ou fetch: consumir API no Node e no navegador
EADDRINUSE: address already in use — resolver no Node
Error: Cannot find module no Node.js: causas e correção
Event loop do Node: uma thread e mil requisições
Perguntas frequentes
Preciso saber JavaScript antes de aprender Node?
Quanto tempo leva para colocar a primeira API no ar?
Express ainda vale a pena em 2026?
Preciso de banco de dados desde a primeira semana?
Node aguenta produção de verdade?
Devo aprender TypeScript junto com Node?
O código deste guia foi executado em Node 24.16.0, Express 5.2.1 e Fastify 5.12.1, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- Node.js Docs — HTTP — nodejs.org
- Express 5 — Roteamento — expressjs.com
- Fastify — Documentation — fastify.dev
- Node.js — Release schedule (LTS) — github.com




