Projeto Prático - Servidor HTTP Simples
INFO
Criado por Kleber Galvão.
Bem-vindo ao Projeto Prático do Módulo 1: Fundamentos do Node.js do curso Node.js do Básico ao Avançado! Nesta aula, você colocará em prática os conceitos aprendidos nas aulas anteriores, criando um servidor HTTP simples usando o módulo nativo http
do Node.js. O projeto inclui a implementação de rotas GET para as páginas inicial (/
), sobre (/about
) e contato (/contact
), além de um Desafio Avançado que adiciona uma rota para ler e retornar o conteúdo de um arquivo JSON.
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.
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
/
,/about
e/contact
com 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.
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.
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 aula de Introdução ao Node.js). - Um projeto Node.js inicializado com
npm init -y
.
Projeto Principal: Criando o Servidor HTTP Simples
Passo 1: Configurar o Projeto
Criar a Pasta do Projeto: Crie uma nova pasta para o projeto e inicialize o
package.json
:bashmkdir servidor-http-simples cd servidor-http-simples npm init -y
Verificar o
package.json
: O comandonpm init -y
cria umpackage.json
com valores padrão:json{ "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
nodemon
para reiniciar o servidor automaticamente durante o desenvolvimento:bashnpm install --save-dev nodemon
Atualize o
package.json
com scripts úteis:json"scripts": { "start": "node index.js", "dev": "nodemon index.js" }
Configurar ES Modules (Opcional): Para usar ES Modules (recomendado em 2025), adicione ao
package.json
:json"type": "module"
Caso prefira CommonJS, pule esta etapa. O exemplo será apresentado em ambos os formatos.
Passo 2: Criar o Servidor HTTP
Criar o Arquivo Principal: Crie um arquivo
index.js
(ouindex.mjs
para ES Modules) na pasta do projeto.Implementar o Servidor:
CommonJS:
javascript// 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:
javascript// 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.createServer
cria 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.statusCode
eres.end
para cada rota. - Status 404 para rotas desconhecidas.
- Status 405 para métodos não suportados.
- Cabeçalho:
Content-Type: text/plain; charset=utf-8
garante que o texto seja exibido corretamente, incluindo caracteres especiais. - Inicialização:
server.listen
inicia o servidor e exibe uma mensagem no console.
- Importação: Usamos
Passo 3: Executar o Servidor
Executar o Projeto:
bashnpm start
Ou, para desenvolvimento com reinício automático:
bashnpm run dev
Testar 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:bash
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"
- Abra um navegador e acesse:
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:
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:bash
git init git add . git commit -m "Servidor HTTP simples com rotas GET"
- Crie um
.gitignore
:node_modules/
- Inicialize um repositório Git:
Depuração:
- Configure o VS Code para depuração:json
{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Launch Program", "program": "${workspaceFolder}/index.js" } ] }
- Adicione breakpoints para inspecionar
req.url
ereq.method
.
- Configure o VS Code para depuração:
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
ab
ouwrk
).
- Evite operações síncronas (ex.:
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.
Passo 1: Criar o Arquivo JSON
Crie um arquivo
data.json
na raiz do projeto com o seguinte conteúdo:json{ "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
).
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:
javascript// 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:
javascript// 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}/`); });
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
fs
permite interagir com o sistema de arquivos (leitura, escrita, etc.).
- Usamos
Rota
/data
:- Verifica se
req.url === '/data'
. - Usa
fs.readFile
para ler o arquivodata.json
de forma assíncrona. - Define o
Content-Type
comoapplication/json
para indicar que a resposta é JSON. - Retorna o conteúdo do arquivo com
res.end(data)
.
- Verifica se
Tratamento de Erros:
- Usa
try/catch
para 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 (
Passo 4: Testar o Desafio
Executar o Servidor:
bashnpm run dev
Testar a Rota
/data
:- No navegador, acesse
http://localhost:3000/data
. Você verá o conteúdo dodata.json
: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:bash
curl http://localhost:3000/data
- No navegador, acesse
Testar Erros:
- Renomeie ou remova o arquivo
data.json
e acessehttp://localhost:3000/data
. Você verá:Erro ao ler o arquivo JSON: ENOENT: no such file or directory...
- Renomeie ou remova o arquivo
Passo 5: Depuração e Melhorias
Depuração:
- Adicione breakpoints no VS Code na linha do
fs.readFile
para inspecionar o conteúdo dedata
. - Use
console.log
para depurar erros:javascriptcatch (error) { console.error('Erro ao ler JSON:', error); res.statusCode = 500; res.end('Erro ao ler o arquivo JSON: ' + error.message + '\n'); }
- Adicione breakpoints no VS Code na linha do
Melhorias Possíveis:
- Validação do JSON: Parse o conteúdo com
JSON.parse
para garantir que é um JSON válido:javascriptconst data = await fs.readFile('./data.json', 'utf-8'); JSON.parse(data); // Lança erro se o JSON for inválido
- Cache: Armazene o conteúdo do JSON em memória para evitar leituras repetidas:javascript
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).
- Validação do JSON: Parse o conteúdo com
Versionamento:
- Atualize o repositório Git:bash
git add . git commit -m "Adicionada rota /data para ler arquivo JSON"
- Atualize o repositório Git:
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ódulofs.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.
Próximos Passos
- Experimente adicionar mais rotas (ex.:
/users
,/products
) ou suportar outros métodos HTTP (ex.: POST). - Explore o módulo
fs
para 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.
Se tiver dúvidas, quiser mais exemplos ou precisar de ajustes no código, é só pedir! 🚀