Leodario.com

Leodario.com – Tudo sobre Tecnologia

Aula 25 – API JavaScript, Node.js e Express – Debugging Node.js – node –inspect, VSCode debugger

Imagem destacada da aula de API

Introdução

Prezados futuros arquitetos de sistemas e mestres das APIs, sejam bem-vindos à nossa Aula 25! Hoje desvendaremos uma das habilidades mais vitais para qualquer desenvolvedor de software: a arte da depuração, ou debugging. Pense na depuração como ser um detetive forense digital. Quando um software falha, ele deixa pistas, vestígios de eventos que levaram ao problema. Nosso papel é analisar essas pistas para entender o que realmente aconteceu, onde o erro se manifestou e, mais importante, como corrigi-lo.

Imagine que você construiu um magnífico carro esportivo (sua API Node.js), mas ele não liga de primeira. Você poderia tentar adivinhar o problema, chutando peças aleatoriamente. Ou, como um mecânico experiente, você conecta ferramentas de diagnóstico (o depurador) para examinar o motor em tempo real, verificando a injeção de combustível, a ignição, a pressão dos pneus (as variáveis, o fluxo de execução). Essa abordagem sistemática é o que o debugging nos permite fazer.

Para APIs modernas, especialmente aquelas construídas com Node.js e Express, a capacidade de identificar e resolver problemas de forma eficiente é imprescindível. Sem ferramentas de depuração adequadas, você passaria horas tentando reproduzir cenários, adicionando inúmeros console.log() – uma técnica rudimentar e demorada. Nesta aula, você aprenderá a usar as poderosas ferramentas de depuração nativas do Node.js, como node --inspect, e a integração perfeita com o VSCode, nosso ambiente de desenvolvimento favorito. Você verá como inspecionar o estado de seu programa, controlar sua execução e solucionar falhas com maestria.

No vasto ecossistema Node.js/Express, a otimização do processo de depuração não apenas economiza tempo valioso, mas também viabiliza a construção de aplicações mais robustas e livres de defeitos. Esta é uma etapa fundamental para evoluir de um codificador iniciante para um engenheiro de software profissional.

Conceito Fundamental

O coração do processo de depuração no Node.js reside no V8 Inspector Protocol. Este é um protocolo de comunicação de baixo nível que o motor JavaScript V8 (o mesmo que alimenta o Chrome e o Node.js) expõe para ferramentas externas. O Node.js habilita este protocolo através da flag--inspect, que inicia um servidor WebSocket dedicado. Esse servidor aguarda conexões de clientes depuradores, como o próprio Chrome DevTools ou o depurador integrado do VSCode.

Quando você executa seu aplicativo Node.js com node --inspect, o ambiente de execução inicia este servidor de depuração, geralmente na porta 9229. Ele então imprime uma URL em seu terminal, algo como ws://127.0.0.1:9229/. Esta URL é o “ponto de entrada” para qualquer ferramenta que queira interagir com seu processo Node.js em modo de depuração. É uma comunicação bidirecional que possibilita a um depurador:

    • Definir pontos de interrupção (breakpoints): Marcar linhas de código onde a execução deve pausar.
    • Inspecionar variáveis: Visualizar o valor de qualquer variável no escopo atual da execução pausada.
    • Percorrer o código (step over, step into, step out): Avançar linha por linha, entrar em chamadas de função ou sair delas.
    • Monitorar a pilha de chamadas (call stack): Ver a sequência de funções que levaram ao ponto atual.
    • Modificar o estado do programa: Em alguns depuradores, é possível até alterar valores de variáveis em tempo de execução.
    • Analisar desempenho e memória: Embora mais avançado, o protocolo V8 também permite a coleta de métricas de performance.

A terminologia correta da indústria inclui breakpoint (ponto de parada), call stack (pilha de chamadas), scope (escopo de variáveis), e stepping (ato de percorrer o código). Estes são termos que você encontrará em qualquer ambiente de depuração e são universais para a resolução de problemas de software.

Em cenários de produção, a depuração direta em servidores geralmente é desaconselhada devido a riscos de segurança e impacto no desempenho. No entanto, o conhecimento do protocolo viabiliza a análise de dumps de memória (heap snapshots) ou o uso de ferramentas de monitoramento de performance que se baseiam em partes desse protocolo para entender o comportamento da aplicação em ambientes reais. O foco principal, contudo, é a depuração durante o desenvolvimento local.

A integração com outras tecnologias é notável. O protocolo V8 é a espinha dorsal de ferramentas como o Chrome DevTools e o VSCode, que oferecem interfaces gráficas intuitivas. Isso facilita a transição de desenvolvedores web front-end para o back-end, pois muitos já estão familiarizados com o DevTools.

Entre as vantagens, destacam-se a economia de tempo, a precisão na identificação de bugs e a compreensão aprofundada do fluxo de execução do programa. A depuração com um depurador é exponencialmente mais eficiente do que o uso indiscriminado de console.log(). Como desvantagem, podemos citar uma pequena sobrecarga de desempenho quando o modo de depuração está ativo, e a necessidade de alguma configuração inicial, especialmente no VSCode. Contudo, os benefícios superam amplamente quaisquer custos iniciais.

Implementação Prática

Vamos agora desenvolver um pequeno aplicativo Express.js e configurá-lo para depuração usando node --inspect e o VSCode. Nosso objetivo é simular um cenário onde um cálculo simples pode ter um erro, e vamos usar o depurador para encontrá-lo.

Primeiro, crie um novo diretório e inicialize um projeto Node.js:

mkdir aula-debugging
cd aula-debugging
npm init -y
npm install express winston

Agora, crie um arquivo chamado server.js com o seguinte conteúdo:

// server.js

// Importa o framework Express para construir nossa API const express = require('express'); // Importa Winston para logging profissional, essencial em ambientes enterprise const winston = require('winston');

// Cria uma instância do aplicativo Express const app = express(); // Define a porta onde a API irá escutar as requisições const PORT = process.env.PORT || 3000;

// Configuração do logger Winston // Utiliza um formato simples para logs no console const logger = winston.createLogger({ level: 'info', // Nível mínimo de log a ser registrado format: winston.format.combine( winston.format.timestamp(), // Adiciona timestamp aos logs winston.format.colorize(), // Colore os logs para melhor leitura no console winston.format.simple() // Formato simples para saída ), transports: [ new winston.transports.Console() // Envia logs para o console ], });

// Middleware para parsing de JSON no corpo das requisições // Vital para APIs que recebem dados via POST/PUT app.use(express.json());

// Exemplo de rota GET simples app.get('/', (req, res) => { logger.info('Requisição recebida na rota raiz.'); // Log da requisição res.send('Bem-vindo à API de Depuração!'); });

/* @function calcularSoma @description Simula uma função que realiza uma soma e pode conter um erro sutil. @param {number} num1 - Primeiro número para a soma. @param {number} num2 - Segundo número para a soma. @returns {number} O resultado da soma. */ function calcularSoma(num1, num2) { logger.info(Iniciando cálculo de soma para ${num1} e ${num2}.); // ERRO INTENCIONAL AQUI: Deveria ser num1 + num2. // Usamos um valor fixo para simular um bug onde o segundo numero e ignorado. const resultado = num1 + 5; // Simula um erro: deveria ser num1 + num2 logger.info(Calculo de soma finalizado: ${resultado}.); return resultado; }

// Rota POST para realizar uma operação de soma app.post('/soma', (req, res) => { logger.info('Requisição de soma recebida.'); // Desestruturação para obter num1 e num2 do corpo da requisição const { num1, num2 } = req.body;

// Validação de entrada robusta: verifica se num1 e num2 são números if (typeof num1 !== 'number' || typeof num2 !== 'number') { logger.warn(Validação falhou: Entradas invalidas (${num1}, ${num2}).); // Retorna um erro 400 (Bad Request) com uma mensagem clara return res.status(400).json({ error: 'Os parâmetros num1 e num2 devem ser números.' }); }

try { const resultadoSoma = calcularSoma(num1, num2); // Chama a função com o bug logger.info(Soma calculada com sucesso: ${num1} + ${num2} = ${resultadoSoma}.); res.json({ num1, num2, resultado: resultadoSoma }); } catch (error) { // Error handling excepcional: registra o erro e retorna uma resposta genérica logger.error(Erro ao processar soma: ${error.message}, error); res.status(500).json({ error: 'Ocorreu um erro interno no servidor ao calcular a soma.' }); } });

// Middleware para tratamento de rotas não encontradas (404) app.use((req, res, next) => { logger.warn(Rota nao encontrada: ${req.method} ${req.originalUrl}); res.status(404).send('Desculpe, a rota que você procura não existe.'); });

// Middleware de tratamento de erros global // Essa e uma boa pratica para capturar erros nao tratados app.use((err, req, res, next) => { logger.error(Erro interno do servidor: ${err.message}, err); res.status(500).json({ error: 'Ocorreu um erro interno no servidor.' }); });

// Inicia o servidor Express app.listen(PORT, () => { logger.info(Servidor Node.js rodando na porta ${PORT}.); logger.info('Para depuracao, use VSCode com um launch.json adequado ou node --inspect.'); });

Configurações Específicas para HostGator Plano M:

Um HostGator Plano M geralmente oferece hospedagem compartilhada, que não facilita ou suporta diretamente a depuração em tempo real de aplicações Node.js no servidor remoto. A depuração remota requer acesso a portas específicas e ferramentas que geralmente não estão disponíveis em planos de hospedagem compartilhada por razões de segurança e recursos. A melhor prática para depuração é sempre realizá-la localmente em sua máquina de desenvolvimento, onde você tem controle total. O código que desenvolvemos é um aplicativo Node.js/Express padrão, que pode ser executado em qualquer ambiente Linux (como o HostGator), mas a depuração que ensinamos é para seu ambiente local.

Depurando com node --inspect

Para iniciar seu aplicativo em modo de depuração, execute o seguinte comando no terminal:

node --inspect server.js

Você verá uma saída similar a esta:

Debugger listening on ws://127.0.0.1:9229/a_long_uuid_string
For help, see: https://nodejs.org/en/docs/inspector
[info]: Servidor Node.js rodando na porta 3000.
[info]: Para depuracao, use VSCode com um launch.json adequado ou node --inspect.

Agora, abra o Google Chrome. Na barra de endereço, digite chrome://inspect e pressione Enter. A página “Devices” será aberta. Na seção “Remote Target”, você deve ver “Node.js” e abaixo a opção “Open dedicated DevTools for Node”. Clique neste link. Uma nova janela do Chrome DevTools se abrirá, conectada ao seu processo Node.js! Você pode ir até a aba “Sources”, navegar até seu server.js e definir breakpoints.

Variação: node --inspect-brk

Se você precisar que o depurador pause a execução na primeira linha do seu código, antes mesmo de qualquer instrução ser executada, use:

node --inspect-brk server.js

Isso é valioso para depurar problemas que ocorrem muito cedo no ciclo de vida da aplicação, como importações ou configurações iniciais.

Depurando com VSCode Debugger

O VSCode oferece uma integração ainda mais intuitiva. Precisamos apenas de um arquivo de configuração launch.json.

    • No VSCode, vá para a aba “Run and Debug” (o ícone de um inseto ou ▶︎ com um inseto).
    • Clique em “create a launch.json file”.
    • Selecione “Node.js”.

O VSCode irá gerar um arquivo .vscode/launch.json. Modifique-o para ter o seguinte conteúdo:

// .vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Lançar Programa via VSCode",
            "skipFiles": [
                "/" // Ignora arquivos internos do Node.js
            ],
            // Caminho para o seu arquivo principal da aplicação
            "program": "${workspaceFolder}/server.js",
            // Reinicia o depurador automaticamente ao salvar alterações
            "restart": true,
            // Argumentos de ambiente (opcional, mas util para definir a porta)
            "env": {
                "PORT": "3000"
            },
            // Argumentos que serao passados ao Node.js
            "runtimeArgs": ["--inspect"],
            // Caminho da pasta de trabalho
            "cwd": "${workspaceFolder}"
        },
        {
            "type": "node",
            "request": "attach",
            "name": "Anexar ao Processo Node.js",
            "skipFiles": [
                "/"
            ],
            // A porta padrao do V8 Inspector
            "port": 9229,
            "address": "localhost"
        }
    ]
}

Com esta configuração, você tem duas opções no VSCode:

    • “Lançar Programa via VSCode” (request: "launch"): O VSCode iniciará o processo Node.js para você no modo de depuração.
    • “Anexar ao Processo Node.js” (request: "attach"): Você inicia o Node.js manualmente com node --inspect (ou npm run dev:debug se tiver configurado um script) e o VSCode se conecta a ele.

Para nosso caso, selecione “Lançar Programa via VSCode” na lista suspensa da aba “Run and Debug” e clique no botão verde de “play”.

Testando e Depurando

1. Abra o arquivo server.js no VSCode.

2. Clique na margem esquerda (ao lado dos números das linhas) na linha const resultado = num1 + 5; dentro da função calcularSoma. Um ponto vermelho aparecerá, indicando um breakpoint.

3. Inicie a depuração usando a configuração “Lançar Programa via VSCode”.

4. Use uma ferramenta como Postman, Insomnia ou curl para enviar uma requisição POST para http://localhost:3000/soma com o corpo JSON:

{
    "num1": 10,
    "num2": 20
}

Exemplo com curl:

curl -X POST -H "Content-Type: application/json" -d '{"num1": 10, "num2": 20}' http://localhost:3000/soma

5. No momento em que a execução atingir o breakpoint, o VSCode pausará. Você poderá:

    • Ver as variáveis no painel “Variables” (num1 será 10, num2 será 20).
    • Ver a “Call Stack” (pilha de chamadas) mostrando como você chegou a essa função.
    • Usar os botões de controle de execução (step over, step into, step out, continue) na barra flutuante para navegar pelo código.

Você notará que resultado será 15 (10 + 5) em vez de 30 (10 + 20), revelando o bug intencional. Agora você pode corrigir a linha para const resultado = num1 + num2;, salvar, e o depurador do VSCode (com "restart": true) provavelmente reiniciará sua aplicação, permitindo que você teste novamente.

Este exemplo demonstra como o depurador é uma ferramenta poderosa para inspecionar o estado do programa e corrigir falhas de forma precisa.

Exercício Hands-On

Desafio Prático: Corrigindo a Validação de Entrada

Nosso endpoint /soma possui uma validação de entrada, mas e se, por algum motivo, um usuário mal-intencionado tentar enviar num1 como uma string vazia "" ou null? Atualmente, nossa validação typeof num1 !== 'number' lidaria com "" mas não com null ou números enviados como strings como "10". Vamos aprimorar essa validação para ser mais robusta, garantindo que os parâmetros sejam estritamente numéricos e não vazios, e usar o depurador para verificar se a nova lógica funciona conforme o esperado.

Tarefa:

    • Modifique a validação do /soma para aceitar apenas números válidos (não null, não undefined, não strings vazias, e que possam ser convertidos para número).
    • Adicione um novo breakpoint na linha da sua validação.
    • Envie requisições com dados problemáticos (ex: {"num1": "10", "num2": null}, {"num1": "abc", "num2": 20}, {"num1": 10} – este último para ver um undefined).
    • Use o depurador para observar os valores de num1 e num2 no momento da validação e verificar se a lógica está correta.

Solução Detalhada Passo a Passo

1. Modificar a Validação em server.js:

Altere a seção de validação na rota /soma para incluir verificações mais rigorosas:

// ... dentro da rota app.post('/soma', ...)

// Validação de entrada robusta aprimorada: // Verifica se num1 e num2 existem, sao do tipo 'number' E nao sao NaN apos conversao. // Tambem lida com 'null' e 'undefined' explicitamente. if (num1 === null || num1 === undefined || typeof num1 !== 'number' || isNaN(num1) || num2 === null || num2 === undefined || typeof num2 !== 'number' || isNaN(num2)) { logger.warn(Validação falhou: Entradas invalidas ou ausentes (${num1}, ${num2}).); return res.status(400).json({ error: 'Os parâmetros num1 e num2 são obrigatórios e devem ser números válidos.' }); }

// ... continue com o try-catch

2. Adicionar Breakpoint:

No VSCode, coloque um breakpoint na primeira linha do seu bloco if de validação (if (num1 === null || ...)).

3. Iniciar a Depuração:

Selecione “Lançar Programa via VSCode” e inicie a depuração (botão verde de “play”).

4. Enviar Requisições Problemáticas (usando curl):

    • Caso 1: num1 como string numérica, num2 como null
      curl -X POST -H "Content-Type: application/json" -d '{"num1": "10", "num2": null}' http://localhost:3000/soma
      

      O depurador deve pausar. Observe em “Variables” que num1 é "10" (string) e num2 é null. A condição typeof num1 !== 'number' ou num2 === null deve ser true, fazendo a validação falhar corretamente.

    • Caso 2: num1 como string inválida, num2 como número
      curl -X POST -H "Content-Type: application/json" -d '{"num1": "abc", "num2": 20}' http://localhost:3000/soma
      

      No breakpoint, num1 será "abc". A condição typeof num1 !== 'number' ou isNaN(num1) (se você tentar converter) garantirá que a validação falhe.

    • Caso 3: num2 ausente (undefined)
      curl -X POST -H "Content-Type: application/json" -d '{"num1": 10}' http://localhost:3000/soma
      

      No breakpoint, num1 será 10 e num2 será undefined. A condição num2 === undefined deve ser true, fazendo a validação falhar.

5. Validar o Resultado:

Em cada caso, o depurador permitirá que você veja exatamente por que a validação falhou, confirmando que sua lógica aprimorada está funcionando. O servidor deve responder com um status 400 e a mensagem de erro que você especificou, indicando uma validação robusta.

Troubleshooting dos Erros Mais Comuns

    • “Debugger listening on ws://… but can’t connect”:
      • Verifique se a porta 9229 não está sendo usada por outro processo.
      • No VSCode, assegure-se de que a configuração port no launch.json esteja correta (9229 é o padrão).
      • No Chrome DevTools, verifique se você clicou em “Open dedicated DevTools for Node” após iniciar o Node.js com --inspect.
    • Breakpoints não são atingidos:
      • Certifique-se de que o arquivo que você está executando é o mesmo que você está depurando. Às vezes, executamos um arquivo antigo por engano.
      • Verifique se o seu código realmente está passando pelas linhas onde os breakpoints foram definidos. Se uma rota não for chamada, o breakpoint nunca será atingido.
      • No VSCode, se um breakpoint aparecer “acinzentado”, significa que o depurador não conseguiu mapear o breakpoint para o código em execução (geralmente problema de caminho de arquivo ou versão do Node.js).
    • Problemas com launch.json:
      • Sintaxe JSON incorreta. Use o validador de JSON do VSCode.
      • Caminho para o program (ex: "${workspaceFolder}/server.js") incorreto. Verifique a localização do seu arquivo principal.

Próximos Passos Sugeridos

    • Aprenda a usar os recursos avançados do depurador: Experimente as “Watch Expressions” para monitorar variáveis específicas, “Conditional Breakpoints” para pausar apenas sob certas condições, e “Logpoints” para registrar valores sem interromper a execução.
    • Explore outras ferramentas de depuração: O Node.js possui ferramentas como ndb (baseado no Chrome DevTools) que oferecem interfaces alternativas.
    • Integre testes de unidade: Embora a depuração seja para encontrar bugs, testes de unidade viabilizam a prevenção de bugs. Combine ambos para um fluxo de trabalho de desenvolvimento robusto.
    • Aprofunde-se no tratamento de erros: Estude padrões de tratamento de erros mais sofisticados para APIs, como centralização de erros e formatação de respostas de erro.

Com estas ferramentas e conhecimentos, você está agora capacitado para depurar suas aplicações Node.js e Express com a confiança de um verdadeiro especialista, desenvolvendo soluções mais confiáveis e de alta qualidade.

🚀 Pronto para a próxima aula?

Continue sua jornada no desenvolvimento de APIs e domine Node.js & Express!

📚 Ver todas as aulas