Skip to content

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

  1. Criar a Pasta do Projeto: Crie uma nova pasta para o projeto e inicialize o package.json:

    bash
    mkdir servidor-http-simples
    cd servidor-http-simples
    npm init -y
  2. Verificar o package.json: O comando npm init -y cria um package.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"
    }
  3. Adicionar Scripts e Nodemon: Instale o nodemon para reiniciar o servidor automaticamente durante o desenvolvimento:

    bash
    npm install --save-dev nodemon

    Atualize o package.json com scripts úteis:

    json
    "scripts": {
      "start": "node index.js",
      "dev": "nodemon index.js"
    }
  4. 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

  1. Criar o Arquivo Principal: Crie um arquivo index.js (ou index.mjs para ES Modules) na pasta do projeto.

  2. 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}/`);
      });
  3. Explicação do Código:

    • Importação: Usamos require('http') (CommonJS) ou import http from 'http' (ES Modules) para importar o módulo http.
    • Configuração: Definimos hostname (127.0.0.1) e port (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 e res.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.

Passo 3: Executar o Servidor

  1. Executar o Projeto:

    bash
    npm start

    Ou, para desenvolvimento com reinício automático:

    bash
    npm run dev
  2. 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"
  3. Saída Esperada:

    • No terminal: Servidor rodando em http://127.0.0.1:3000/.
    • No navegador ou curl: As mensagens correspondentes a cada rota.

Passo 4: Boas Práticas

  1. 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).
  2. 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).
  3. 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/
  4. 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 e req.method.
  5. 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 ou wrk).

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

  1. 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"
        ]
      }
    }
  2. Verifique se o arquivo está na mesma pasta que index.js (ou index.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.).
  • Rota /data:

    • Verifica se req.url === '/data'.
    • Usa fs.readFile para ler o arquivo data.json de forma assíncrona.
    • Define o Content-Type como application/json para indicar que a resposta é JSON.
    • Retorna o conteúdo do arquivo com res.end(data).
  • 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.
  • 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.

Passo 4: Testar o Desafio

  1. Executar o Servidor:

    bash
    npm run dev
  2. Testar a Rota /data:

    • No navegador, acesse http://localhost:3000/data. Você verá o conteúdo do data.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
  3. Testar Erros:

    • Renomeie ou remova o arquivo data.json e acesse http://localhost:3000/data. Você verá:
      Erro ao ler o arquivo JSON: ENOENT: no such file or directory...

Passo 5: Depuração e Melhorias

  1. Depuração:

    • Adicione breakpoints no VS Code na linha do fs.readFile para inspecionar o conteúdo de data.
    • Use console.log para depurar erros:
      javascript
      catch (error) {
        console.error('Erro ao ler JSON:', error);
        res.statusCode = 500;
        res.end('Erro ao ler o arquivo JSON: ' + error.message + '\n');
      }
  2. Melhorias Possíveis:

    • Validação do JSON: Parse o conteúdo com JSON.parse para garantir que é um JSON válido:
      javascript
      const 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).
  3. Versionamento:

    • Atualize o repositório Git:
      bash
      git add .
      git commit -m "Adicionada rota /data para ler arquivo JSON"

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.

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! 🚀

Todos os direitos reservados.