16 Projeto Prático - Servidor HTTP Simples
O objetivo é consolidar sua compreensão sobre como o Node.js lida com requisições HTTP, como estruturar um servidor básico e como integrar manipulação de arquivos com o módulo fs. O material é dividido em duas partes: o Projeto Principal, que implementa o servidor com as rotas especificadas, e o Desafio Avançado, que estende a funcionalidade com leitura de arquivos JSON. O conteúdo é prático, com exemplos comentados, boas práticas e explicações detalhadas para garantir um aprendizado sólido.
16.1 Introdução ao Projeto
O módulo http do Node.js permite criar servidores web que respondem a requisições HTTP, como GET, POST, entre outros. Neste projeto, você criará um servidor que:
- Escuta na porta 3000.
- Responde a requisições GET nas rotas
/,/aboute/contactcom mensagens de texto. - Retorna um erro 404 para rotas desconhecidas.
- (No Desafio Avançado) Lê um arquivo JSON e retorna seu conteúdo em uma rota
/data.
Este projeto é uma introdução prática à construção de servidores web com Node.js, preparando você para tópicos mais avançados, como APIs RESTful com Express. Vamos usar o módulo http para manter a simplicidade e focar nos fundamentos, e o módulo fs (File System) para o Desafio Avançado.
16.1.1 Objetivos do Projeto
- Criar um servidor HTTP funcional com o módulo
http. - Implementar rotas GET básicas (
/,/about,/contact). - Lidar com erros (ex.: 404 para rotas inválidas).
- (Desafio Avançado) Integrar leitura de arquivos JSON com o módulo
fs. - Aplicar boas práticas, como organização de código, tratamento de erros e versionamento com Git.
16.1.2 Pré-requisitos
- Node.js (versão LTS, v20.x ou superior em 2025) instalado.
- Visual Studio Code (VS Code) configurado com extensões recomendadas (ESLint, Prettier).
- Conhecimento básico de JavaScript e do módulo
http(conforme a Introdução ao Node.js). - Um projeto Node.js inicializado com
npm init -y.
16.2 Projeto Principal: Criando o Servidor HTTP Simples
16.2.1 Passo 1: Configurar o Projeto
Criar a Pasta do Projeto: Crie uma nova pasta para o projeto e inicialize o
package.json:mkdir servidor-http-simples cd servidor-http-simples npm init -yVerificar o
package.json: O comandonpm init -ycria umpackage.jsoncom valores padrão:{ "name": "servidor-http-simples", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }Adicionar Scripts e Nodemon: Instale o
nodemonpara reiniciar o servidor automaticamente durante o desenvolvimento:npm install --save-dev nodemonAtualize o
package.jsoncom scripts úteis:"scripts": { "start": "node index.js", "dev": "nodemon index.js" }Configurar ES Modules (Opcional): Para usar ES Modules (recomendado em 2025), adicione ao
package.json:"type": "module"Caso prefira CommonJS, pule esta etapa. O exemplo será apresentado em ambos os formatos.
16.2.2 Passo 2: Criar o Servidor HTTP
Criar o Arquivo Principal: Crie um arquivo
index.js(ouindex.mjspara ES Modules) na pasta do projeto.Implementar o Servidor:
CommonJS:
// index.js const http = require('http'); const hostname = '127.0.0.1'; const port = 3000; const server = http.createServer((req, res) => { res.setHeader('Content-Type', 'text/plain; charset=utf-8'); if (req.method === 'GET') { if (req.url === '/') { res.statusCode = 200; res.end('Bem-vindo à página inicial!\n'); } else if (req.url === '/about') { res.statusCode = 200; res.end('Sobre nós: Somos uma equipe apaixonada por tecnologia!\n'); } else if (req.url === '/contact') { res.statusCode = 200; res.end('Contato: Envie um e-mail para contato@exemplo.com\n'); } else { res.statusCode = 404; res.end('Página não encontrada!\n'); } } else { res.statusCode = 405; res.end('Método não permitido. Use GET.\n'); } }); server.listen(port, hostname, () => { console.log(`Servidor rodando em http://${hostname}:${port}/`); });ES Modules:
// index.mjs import http from 'http'; const hostname = '127.0.0.1'; const port = 3000; const server = http.createServer((req, res) => { res.setHeader('Content-Type', 'text/plain; charset=utf-8'); if (req.method === 'GET') { if (req.url === '/') { res.statusCode = 200; res.end('Bem-vindo à página inicial!\n'); } else if (req.url === '/about') { res.statusCode = 200; res.end('Sobre nós: Somos uma equipe apaixonada por tecnologia!\n'); } else if (req.url === '/contact') { res.statusCode = 200; res.end('Contato: Envie um e-mail para contato@exemplo.com\n'); } else { res.statusCode = 404; res.end('Página não encontrada!\n'); } } else { res.statusCode = 405; res.end('Método não permitido. Use GET.\n'); } }); server.listen(port, hostname, () => { console.log(`Servidor rodando em http://${hostname}:${port}/`); });
Explicação do Código:
- Importação: Usamos
require('http')(CommonJS) ouimport http from 'http'(ES Modules) para importar o módulohttp. - Configuração: Definimos
hostname(127.0.0.1) eport(3000) para o servidor. - Criação do Servidor:
http.createServercria o servidor, recebendo uma função de callback que lida com requisições (req) e respostas (res). - Rotas:
req.method === 'GET': Verifica se a requisição é do tipo GET.req.url: Verifica a URL solicitada (/,/about,/contact).- Respostas com
res.statusCodeeres.endpara cada rota. - Status 404 para rotas desconhecidas.
- Status 405 para métodos não suportados.
- Cabeçalho:
Content-Type: text/plain; charset=utf-8garante que o texto seja exibido corretamente, incluindo caracteres especiais. - Inicialização:
server.listeninicia o servidor e exibe uma mensagem no console.
- Importação: Usamos
16.2.3 Passo 3: Executar o Servidor
Executar o Projeto:
npm startOu, para desenvolvimento com reinício automático:
npm run devTestar as Rotas:
Abra um navegador e acesse:
http://localhost:3000/→ “Bem-vindo à página inicial!”http://localhost:3000/about→ “Sobre nós: Somos uma equipe apaixonada por tecnologia!”http://localhost:3000/contact→ “Contato: Envie um e-mail para contato@exemplo.com”http://localhost:3000/qualquercoisa→ “Página não encontrada!”
Use o Postman ou curl para testar:
curl http://localhost:3000/ curl http://localhost:3000/about curl http://localhost:3000/contact curl -X POST http://localhost:3000/ # Deve retornar "Método não permitido"
Saída Esperada:
- No terminal:
Servidor rodando em http://127.0.0.1:3000/. - No navegador ou curl: As mensagens correspondentes a cada rota.
- No terminal:
16.2.4 Passo 4: Boas Práticas
- Organização do Código:
- Use constantes para valores fixos (
hostname,port). - Adicione comentários claros para facilitar a manutenção.
- Mantenha a lógica de rotas simples e modular (futuras aulas usarão Express para isso).
- Use constantes para valores fixos (
- Tratamento de Erros:
- Incluímos status 404 e 405 para lidar com rotas inválidas e métodos não suportados.
- Considere adicionar logs para erros (ex.:
console.error).
- Versionamento:
Inicialize um repositório Git:
git init git add . git commit -m "Servidor HTTP simples com rotas GET"Crie um
.gitignore:node_modules/
- Depuração:
Configure o VS Code para depuração:
{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Launch Program", "program": "${workspaceFolder}/index.js" } ] }Adicione breakpoints para inspecionar
req.urlereq.method.
- Performance:
- Evite operações síncronas (ex.:
fs.readFileSync) no servidor, pois bloqueiam o Event Loop. - Teste o servidor com múltiplas requisições simultâneas (ex.: usando ferramentas como
abouwrk).
- Evite operações síncronas (ex.:
16.3 Desafio Avançado: Adicionar uma Rota que Lê um Arquivo JSON
Neste desafio, você adicionará uma rota /data que lê um arquivo JSON do sistema de arquivos e retorna seu conteúdo como resposta. Isso introduz o uso do módulo fs (File System) do Node.js e demonstra como integrar dados persistentes em um servidor HTTP.
16.3.1 Passo 1: Criar o Arquivo JSON
Crie um arquivo
data.jsonna raiz do projeto com o seguinte conteúdo:{ "site": { "name": "Meu Site", "version": "1.0.0", "features": [ "Página inicial dinâmica", "Seção sobre a equipe", "Formulário de contato" ] } }Verifique se o arquivo está na mesma pasta que
index.js(ouindex.mjs).
16.3.2 Passo 2: Atualizar o Servidor
Modifique o arquivo index.js (ou index.mjs) para incluir a rota /data que lê o arquivo JSON usando o módulo fs.
CommonJS:
// index.js const http = require('http'); const fs = require('fs').promises; // Usar versão assíncrona do fs const hostname = '127.0.0.1'; const port = 3000; const server = http.createServer(async (req, res) => { // Definir cabeçalho padrão res.setHeader('Content-Type', 'text/plain; charset=utf-8'); if (req.method === 'GET') { if (req.url === '/') { res.statusCode = 200; res.end('Bem-vindo à página inicial!\n'); } else if (req.url === '/about') { res.statusCode = 200; res.end('Sobre nós: Somos uma equipe apaixonada por tecnologia!\n'); } else if (req.url === '/contact') { res.statusCode = 200; res.end('Contato: Envie um e-mail para contato@exemplo.com\n'); } else if (req.url === '/data') { try { // Ler o arquivo JSON de forma assíncrona const data = await fs.readFile('./data.json', 'utf-8'); // Alterar o Content-Type para JSON res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.statusCode = 200; res.end(data); } catch (error) { res.statusCode = 500; res.end('Erro ao ler o arquivo JSON: ' + error.message + '\n'); } } else { res.statusCode = 404; res.end('Página não encontrada!\n'); } } else { res.statusCode = 405; res.end('Método não permitido. Use GET.\n'); } }); server.listen(port, hostname, () => { console.log(`Servidor rodando em http://${hostname}:${port}/`); });ES Modules:
// index.mjs import http from 'http'; import { promises as fs } from 'fs'; const hostname = '127.0.0.1'; const port = 3000; const server = http.createServer(async (req, res) => { res.setHeader('Content-Type', 'text/plain; charset=utf-8'); if (req.method === 'GET') { if (req.url === '/') { res.statusCode = 200; res.end('Bem-vindo à página inicial!\n'); } else if (req.url === '/about') { res.statusCode = 200; res.end('Sobre nós: Somos uma equipe apaixonada por tecnologia!\n'); } else if (req.url === '/contact') { res.statusCode = 200; res.end('Contato: Envie um e-mail para contato@exemplo.com\n'); } else if (req.url === '/data') { try { const data = await fs.readFile('./data.json', 'utf-8'); res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.statusCode = 200; res.end(data); } catch (error) { res.statusCode = 500; res.end('Erro ao ler o arquivo JSON: ' + error.message + '\n'); } } else { res.statusCode = 404; res.end('Página não encontrada!\n'); } } else { res.statusCode = 405; res.end('Método não permitido. Use GET.\n'); } }); server.listen(port, hostname, () => { console.log(`Servidor rodando em http://${hostname}:${port}/`); });
16.3.3 Passo 3: Explicação do Código do Desafio
- Importação do Módulo
fs:- Usamos
fs.promises(CommonJS:require('fs').promises, ES Modules:import { promises as fs } from 'fs') para operações assíncronas, evitando bloqueios no Event Loop. - O módulo
fspermite interagir com o sistema de arquivos (leitura, escrita, etc.).
- Usamos
- Rota
/data:- Verifica se
req.url === '/data'. - Usa
fs.readFilepara ler o arquivodata.jsonde forma assíncrona. - Define o
Content-Typecomoapplication/jsonpara indicar que a resposta é JSON. - Retorna o conteúdo do arquivo com
res.end(data).
- Verifica se
- Tratamento de Erros:
- Usa
try/catchpara capturar erros (ex.: arquivo não encontrado). - Retorna status 500 com uma mensagem de erro se a leitura falhar.
- Usa
- Boas Práticas:
- Usa operações assíncronas (
fs.promises) para manter o servidor não-bloqueante. - Define o charset (
utf-8) para suportar caracteres especiais. - Mantém o código modular e comentado.
- Usa operações assíncronas (
16.3.4 Passo 4: Testar o Desafio
Executar o Servidor:
npm run devTestar a Rota
/data:No navegador, acesse
http://localhost:3000/data. Você verá o conteúdo dodata.json:{ "site": { "name": "Meu Site", "version": "1.0.0", "features": [ "Página inicial dinâmica", "Seção sobre a equipe", "Formulário de contato" ] } }Com curl:
curl http://localhost:3000/data
Testar Erros:
Renomeie ou remova o arquivo
data.jsone acessehttp://localhost:3000/data. Você verá:Erro ao ler o arquivo JSON: ENOENT: no such file or directory...
16.3.5 Passo 5: Depuração e Melhorias
- Depuração:
Adicione breakpoints no VS Code na linha do
fs.readFilepara inspecionar o conteúdo dedata.Use
console.logpara depurar erros:catch (error) { console.error('Erro ao ler JSON:', error); res.statusCode = 500; res.end('Erro ao ler o arquivo JSON: ' + error.message + '\n'); }
- Melhorias Possíveis:
Validação do JSON: Parse o conteúdo com
JSON.parsepara garantir que é um JSON válido:const data = await fs.readFile('./data.json', 'utf-8'); JSON.parse(data); // Lança erro se o JSON for inválidoCache: Armazene o conteúdo do JSON em memória para evitar leituras repetidas:
let cache = null; if (cache) { res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.statusCode = 200; res.end(cache); } else { const data = await fs.readFile('./data.json', 'utf-8'); cache = data; res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.statusCode = 200; res.end(data); }Modularização: Mova a lógica de rotas para um arquivo separado (explorado em módulos futuros com Express).
- Versionamento:
Atualize o repositório Git:
git add . git commit -m "Adicionada rota /data para ler arquivo JSON"
16.4 Conclusão
Neste projeto prático, você: - Criou um servidor HTTP simples com o módulo http, implementando rotas GET para /, /about e /contact. - Lidaram com erros (404 para rotas inválidas, 405 para métodos não suportados). - Completou o Desafio Avançado, adicionando uma rota /data que lê um arquivo JSON com o módulo fs.promises. - Aplicou boas práticas, como tratamento de erros, uso de operações assíncronas e versionamento com Git.
Este projeto consolida os fundamentos do Node.js, como o Event Loop, requisições HTTP e manipulação de arquivos, preparando você para tópicos mais avançados, como APIs com Express e integração com bancos de dados.
16.4.1 Próximos Passos
- Experimente adicionar mais rotas (ex.:
/users,/products) ou suportar outros métodos HTTP (ex.: POST). - Explore o módulo
fspara outras operações (ex.: escrita de arquivos). - Prepare-se para o Módulo 2, onde abordaremos o Sistema de Arquivos (fs module) e operações assíncronas em profundidade.