JavaScript

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

8 min de leitura

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

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

<h2>Introdução ao HTTP Nativo em Node.js</h2>

<p>Node.js oferece o módulo <code>http</code> 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.</p>

<h2>Criando Seu Primeiro Servidor</h2>

<h3>Servidor Mínimo Funcional</h3>

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

<pre><code class="language-javascript">const http = require(&#039;http&#039;);

const server = http.createServer((req, res) =&gt; {

res.writeHead(200, { &#039;Content-Type&#039;: &#039;text/plain&#039; });

res.end(&#039;Olá, Mundo!&#039;);

});

server.listen(3000, () =&gt; {

console.log(&#039;Servidor rodando em http://localhost:3000&#039;);

});</code></pre>

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

<h3>Entendendo Request e Response</h3>

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

<h2>Roteamento e Métodos HTTP</h2>

<h3>Implementando Rotas Básicas</h3>

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

<pre><code class="language-javascript">const http = require(&#039;http&#039;);

const server = http.createServer((req, res) =&gt; {

const { method, url } = req;

if (url === &#039;/&#039; &amp;&amp; method === &#039;GET&#039;) {

res.writeHead(200, { &#039;Content-Type&#039;: &#039;application/json&#039; });

res.end(JSON.stringify({ mensagem: &#039;Home&#039; }));

}

else if (url === &#039;/usuarios&#039; &amp;&amp; method === &#039;GET&#039;) {

res.writeHead(200, { &#039;Content-Type&#039;: &#039;application/json&#039; });

res.end(JSON.stringify([{ id: 1, nome: &#039;João&#039; }]));

}

else if (url === &#039;/usuarios&#039; &amp;&amp; method === &#039;POST&#039;) {

res.writeHead(201, { &#039;Content-Type&#039;: &#039;application/json&#039; });

res.end(JSON.stringify({ id: 2, nome: &#039;Maria&#039; }));

}

else {

res.writeHead(404, { &#039;Content-Type&#039;: &#039;application/json&#039; });

res.end(JSON.stringify({ erro: &#039;Rota não encontrada&#039; }));

}

});

server.listen(3000, () =&gt; {

console.log(&#039;Servidor rodando em http://localhost:3000&#039;);

});</code></pre>

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

<h2>Processando Corpo de Requisições</h2>

<h3>Recebendo e Parsing de JSON</h3>

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

<pre><code class="language-javascript">const http = require(&#039;http&#039;);

function parseBody(req) {

return new Promise((resolve, reject) =&gt; {

let data = &#039;&#039;;

req.on(&#039;data&#039;, chunk =&gt; {

data += chunk.toString();

});

req.on(&#039;end&#039;, () =&gt; {

try {

resolve(JSON.parse(data));

} catch {

reject(new Error(&#039;JSON inválido&#039;));

}

});

req.on(&#039;error&#039;, reject);

});

}

const server = http.createServer(async (req, res) =&gt; {

try {

if (req.url === &#039;/usuarios&#039; &amp;&amp; req.method === &#039;POST&#039;) {

const body = await parseBody(req);

res.writeHead(201, { &#039;Content-Type&#039;: &#039;application/json&#039; });

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, { &#039;Content-Type&#039;: &#039;application/json&#039; });

res.end(JSON.stringify({ erro: err.message }));

}

});

server.listen(3000);</code></pre>

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

<h3>Validação e Segurança Básica</h3>

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

<pre><code class="language-javascript">function parseBody(req, maxSize = 1e6) {

return new Promise((resolve, reject) =&gt; {

let data = &#039;&#039;;

let size = 0;

req.on(&#039;data&#039;, chunk =&gt; {

size += chunk.length;

if (size &gt; maxSize) {

reject(new Error(&#039;Corpo muito grande&#039;));

}

data += chunk.toString();

});

req.on(&#039;end&#039;, () =&gt; {

try {

resolve(JSON.parse(data));

} catch {

reject(new Error(&#039;JSON inválido&#039;));

}

});

});

}</code></pre>

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

<h2>Exemplo Prático: API REST Simples</h2>

<h3>Servidor Completo com CRUD</h3>

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

<pre><code class="language-javascript">const http = require(&#039;http&#039;);

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

function parseBody(req) {

return new Promise((resolve, reject) =&gt; {

let data = &#039;&#039;;

req.on(&#039;data&#039;, chunk =&gt; { data += chunk; });

req.on(&#039;end&#039;, () =&gt; {

try {

resolve(JSON.parse(data || &#039;{}&#039;));

} catch {

reject(new Error(&#039;JSON inválido&#039;));

}

});

});

}

function respond(res, status, data) {

res.writeHead(status, { &#039;Content-Type&#039;: &#039;application/json&#039; });

res.end(JSON.stringify(data));

}

const server = http.createServer(async (req, res) =&gt; {

const [, rota, id] = req.url.split(&#039;/&#039;);

try {

if (rota === &#039;usuarios&#039; &amp;&amp; req.method === &#039;GET&#039;) {

respond(res, 200, usuarios);

}

else if (rota === &#039;usuarios&#039; &amp;&amp; req.method === &#039;POST&#039;) {

const { nome } = await parseBody(req);

const novoUsuario = { id: Date.now(), nome };

usuarios.push(novoUsuario);

respond(res, 201, novoUsuario);

}

else if (rota === &#039;usuarios&#039; &amp;&amp; req.method === &#039;DELETE&#039;) {

usuarios = usuarios.filter(u =&gt; u.id != id);

respond(res, 200, { ok: true });

}

else {

respond(res, 404, { erro: &#039;Não encontrado&#039; });

}

} catch (err) {

respond(res, 400, { erro: err.message });

}

});

server.listen(3000, () =&gt; console.log(&#039;Servidor em 3000&#039;));</code></pre>

<p>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.</p>

<h2>Conclusão</h2>

<p>Nesta aula, aprendemos três conceitos fundamentais: <strong>primeiro</strong>, como criar servidores HTTP nativos sem frameworks, entendendo request/response; <strong>segundo</strong>, implementar roteamento e processamento de diferentes métodos HTTP de forma manual; <strong>terceiro</strong>, 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.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://nodejs.org/api/http.html" target="_blank" rel="noopener noreferrer">Node.js HTTP Documentation</a></li>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP" target="_blank" rel="noopener noreferrer">MDN Web Docs - HTTP Protocol</a></li>

<li><a href="https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction/" target="_blank" rel="noopener noreferrer">Node.js Official Guide</a></li>

<li><a href="https://restfulapi.net/" target="_blank" rel="noopener noreferrer">REST API Best Practices - Restfulapi.net</a></li>

<li><a href="https://javascript.info/fetch" target="_blank" rel="noopener noreferrer">JavaScript.info - Fetch API and HTTP</a></li>

</ul>

Comentários

Mais em JavaScript

Dominando Hooks em React: useState, useEffect, useRef e useCallback em Projetos Reais
Dominando Hooks em React: useState, useEffect, useRef e useCallback em Projetos Reais

useState: Gerenciando Estado em Componentes Funcionais O é o hook fundamental...

Como Usar Web Storage em JavaScript: localStorage, sessionStorage e IndexedDB em Produção
Como Usar Web Storage em JavaScript: localStorage, sessionStorage e IndexedDB em Produção

Web Storage em JavaScript: localStorage, sessionStorage e IndexedDB A persist...

Como Usar Objetos em JavaScript: Criação, Propriedades e Métodos em Produção
Como Usar Objetos em JavaScript: Criação, Propriedades e Métodos em Produção

Fundamentos de Objetos em JavaScript Um objeto em JavaScript é uma estrutura...