HTTP Nativo em Node.js: Criando Servidores sem Framework na Prática Já leu

Introdução ao HTTP Nativo em Node.js Node.js oferece o módulo nativo, permitindo criar servidores web poderosos sem dependências externas. Muitos desenvolvedores pulam direto para frameworks como Express, mas entender como o HTTP funciona no nível mais baixo é essencial para dominar programação backend. Nesta aula, exploraremos desde a criação de um servidor básico até o tratamento profissional de requisições, sem usar nenhum framework. Criando Seu Primeiro Servidor Servidor Mínimo Funcional O Node.js inclui o módulo por padrão. Para criar um servidor, precisamos apenas importá-lo e definir um handler para requisições: Esse código cria um servidor que responde a qualquer requisição com "Olá, Mundo!". O callback recebe dois parâmetros: (requisição) e (resposta). Salve como e execute com . Acesse no navegador para ver funcionando. Entendendo Request e Response O objeto contém informações sobre a requisição: método HTTP, caminho, headers, etc. O objeto é usado para enviar dados ao cliente. A sequência típica é: para definir status e headers, depois para enviar

Introdução ao HTTP Nativo em Node.js

Node.js oferece o módulo http nativo, permitindo criar servidores web poderosos sem dependências externas. Muitos desenvolvedores pulam direto para frameworks como Express, mas entender como o HTTP funciona no nível mais baixo é essencial para dominar programação backend. Nesta aula, exploraremos desde a criação de um servidor básico até o tratamento profissional de requisições, sem usar nenhum framework.

Criando Seu Primeiro Servidor

Servidor Mínimo Funcional

O Node.js inclui o módulo http por padrão. Para criar um servidor, precisamos apenas importá-lo e definir um handler para requisições:

const http = require('http');

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Olá, Mundo!');
});

server.listen(3000, () => {
  console.log('Servidor rodando em http://localhost:3000');
});

Esse código cria um servidor que responde a qualquer requisição com "Olá, Mundo!". O callback recebe dois parâmetros: req (requisição) e res (resposta). Salve como servidor.js e execute com node servidor.js. Acesse http://localhost:3000 no navegador para ver funcionando.

Entendendo Request e Response

O objeto req contém informações sobre a requisição: método HTTP, caminho, headers, etc. O objeto res é usado para enviar dados ao cliente. A sequência típica é: writeHead() para definir status e headers, depois write() para enviar corpo, e end() para finalizar. Sempre termine com end(), caso contrário o cliente ficará aguardando.

Roteamento e Métodos HTTP

Implementando Rotas Básicas

A maioria dos servidores precisa diferenciar requisições por caminho (rota) e método HTTP. Vamos criar um roteador simples:

const http = require('http');

const server = http.createServer((req, res) => {
  const { method, url } = req;

  if (url === '/' && method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ mensagem: 'Home' }));
  } 
  else if (url === '/usuarios' && method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify([{ id: 1, nome: 'João' }]));
  }
  else if (url === '/usuarios' && method === 'POST') {
    res.writeHead(201, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ id: 2, nome: 'Maria' }));
  }
  else {
    res.writeHead(404, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ erro: 'Rota não encontrada' }));
  }
});

server.listen(3000, () => {
  console.log('Servidor rodando em http://localhost:3000');
});

Aqui diferenciamos por method e url. GET em /usuarios retorna uma lista, POST cria um novo usuário. Rotas não encontradas retornam 404. Este é o padrão base de qualquer servidor HTTP.

Processando Corpo de Requisições

Recebendo e Parsing de JSON

Requisições POST e PUT frequentemente contêm dados no corpo. Precisamos ler o stream e fazer parsing:

const http = require('http');

function parseBody(req) {
  return new Promise((resolve, reject) => {
    let data = '';

    req.on('data', chunk => {
      data += chunk.toString();
    });

    req.on('end', () => {
      try {
        resolve(JSON.parse(data));
      } catch {
        reject(new Error('JSON inválido'));
      }
    });

    req.on('error', reject);
  });
}

const server = http.createServer(async (req, res) => {
  try {
    if (req.url === '/usuarios' && req.method === 'POST') {
      const body = await parseBody(req);

      res.writeHead(201, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({
        id: Math.random(),
        nome: body.nome,
        criado: new Date().toISOString()
      }));
    } else {
      res.writeHead(404);
      res.end();
    }
  } catch (err) {
    res.writeHead(400, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ erro: err.message }));
  }
});

server.listen(3000);

O módulo http trabalha com streams. Quando há dados no corpo, o evento data é disparado com chunks. Quando termina, end é disparado. Convertemos tudo a string, fazemos parse JSON e retornamos uma resposta apropriada. Use o método Promise para trabalhar com callbacks assincronamente.

Validação e Segurança Básica

Sempre valide dados recebidos. Estabeleça limite de tamanho para proteger contra ataques:

function parseBody(req, maxSize = 1e6) {
  return new Promise((resolve, reject) => {
    let data = '';
    let size = 0;

    req.on('data', chunk => {
      size += chunk.length;
      if (size > maxSize) {
        reject(new Error('Corpo muito grande'));
      }
      data += chunk.toString();
    });

    req.on('end', () => {
      try {
        resolve(JSON.parse(data));
      } catch {
        reject(new Error('JSON inválido'));
      }
    });
  });
}

Defina um máximo de bytes (aqui 1MB). Se exceder, rejeite imediatamente. Isso evita consumo excessivo de memória.

Exemplo Prático: API REST Simples

Servidor Completo com CRUD

Vamos unir tudo em uma API REST básica que gerencia usuários em memória:

const http = require('http');

let usuarios = [{ id: 1, nome: 'João' }];

function parseBody(req) {
  return new Promise((resolve, reject) => {
    let data = '';
    req.on('data', chunk => { data += chunk; });
    req.on('end', () => {
      try {
        resolve(JSON.parse(data || '{}'));
      } catch {
        reject(new Error('JSON inválido'));
      }
    });
  });
}

function respond(res, status, data) {
  res.writeHead(status, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify(data));
}

const server = http.createServer(async (req, res) => {
  const [, rota, id] = req.url.split('/');

  try {
    if (rota === 'usuarios' && req.method === 'GET') {
      respond(res, 200, usuarios);
    }
    else if (rota === 'usuarios' && req.method === 'POST') {
      const { nome } = await parseBody(req);
      const novoUsuario = { id: Date.now(), nome };
      usuarios.push(novoUsuario);
      respond(res, 201, novoUsuario);
    }
    else if (rota === 'usuarios' && req.method === 'DELETE') {
      usuarios = usuarios.filter(u => u.id != id);
      respond(res, 200, { ok: true });
    }
    else {
      respond(res, 404, { erro: 'Não encontrado' });
    }
  } catch (err) {
    respond(res, 400, { erro: err.message });
  }
});

server.listen(3000, () => console.log('Servidor em 3000'));

Este exemplo mostra GET (listar), POST (criar), DELETE (remover). Os dados ficam em memória, então são perdidos ao reiniciar. Para produção, use um banco de dados real.

Conclusão

Nesta aula, aprendemos três conceitos fundamentais: primeiro, como criar servidores HTTP nativos sem frameworks, entendendo request/response; segundo, implementar roteamento e processamento de diferentes métodos HTTP de forma manual; terceiro, trabalhar com streams e dados assincronamente, essencial para qualquer servidor profissional. Dominar HTTP nativo transforma você em um desenvolvedor mais completo, capaz de entender o que frameworks abstraem. Use esse conhecimento para escolher entre soluções prontas ou personalizadas conforme necessário.

Referências


Artigos relacionados