Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA

Aula 6 de 6

Projeto Node.js: API de tarefas do começo ao fim

Reúna módulos, HTTP, JSON, validação e tratamento de erros em uma API de tarefas que você consegue testar com curl do início ao fim.

70 minutos · leitura + prática · nível iniciante

Ao terminar esta aula, você vai conseguir

  • Organizar servidor, serviço e persistência por responsabilidade
  • Ler corpo JSON com limite e validar uma tarefa
  • Verificar os fluxos de listar, criar e falhar
Uma requisição sai do navegador, passa pelo Node.js e volta como JSON.

Chegou a hora de conectar as peças numa API pequena, mas verificável. O projeto terá três responsabilidades: o servidor traduz HTTP, o serviço aplica regras de tarefas e o repositório lê e grava JSON. Essa divisão não existe para aumentar o número de arquivos; ela deixa claro onde cada mudança deve acontecer.

Pense no fluxo de um pedido: atendimento registra, cozinha aplica o processo e estoque mantém os dados dos ingredientes. Na API, transporte, regra e persistência também colaboram por contratos. O limite é que software permite outras arquiteturas; três camadas são uma escolha didática, não uma lei universal.

Defina o contrato antes das rotas

Nossa tarefa terá id, titulo e concluida. A criação aceita apenas um título não vazio. O serviço pode ser pequeno:

js
export function validarTitulo(valor) {
  if (typeof valor !== 'string' || valor.trim().length < 3) {
    const erro = new Error('Título precisa ter pelo menos 3 caracteres');
    erro.statusCode = 400;
    throw erro;
  }
  return valor.trim();
}

export function criarTarefa(titulo) {
  return { id: crypto.randomUUID(), titulo: validarTitulo(titulo), concluida: false };
}

O erro controlado carrega um status para a borda HTTP traduzir. O serviço não recebe req nem chama res.end; assim, a regra pode ser testada sem rede.

Leia o corpo com um limite

No módulo HTTP nativo, o corpo chega em partes chamadas chunks. Reúna as partes, imponha um limite e só então interprete JSON:

js
export async function lerJson(req, limite = 10_000) {
  const partes = [];
  let tamanho = 0;

  for await (const parte of req) {
    tamanho += parte.length;
    if (tamanho > limite) {
      const erro = new Error('Corpo excede o limite');
      erro.statusCode = 413;
      throw erro;
    }
    partes.push(parte);
  }

  try {
    return JSON.parse(Buffer.concat(partes).toString('utf8'));
  } catch {
    const erro = new Error('JSON inválido');
    erro.statusCode = 400;
    throw erro;
  }
}

O limite evita manter um corpo arbitrariamente grande em memória. Em produção, outros limites, autenticação, autorização, TLS, logs e proteção de abuso ainda são necessários; este projeto cobre o fluxo essencial, não toda a segurança.

Ligue rotas ao repositório

Supondo que listarTarefas e salvarTarefas usem as funções da aula anterior, o roteador pode tratar consulta e criação:

js
async function rotear(req, res) {
  if (req.method === 'GET' && req.url === '/tarefas') {
    return responder(res, 200, await listarTarefas());
  }

  if (req.method === 'POST' && req.url === '/tarefas') {
    const entrada = await lerJson(req);
    const tarefas = await listarTarefas();
    const nova = criarTarefa(entrada.titulo);
    await salvarTarefas([...tarefas, nova]);
    return responder(res, 201, nova);
  }

  return responder(res, 404, { erro: 'Rota não encontrada' });
}

A função responder define status, content-type e serializa uma única vez. O servidor envolve rotear com try/catch: erros controlados usam erro.statusCode; falhas inesperadas são registradas no servidor e devolvem uma mensagem genérica com status 500, sem expor detalhes internos.

Siga uma requisição completa com o dedo no código: createServer recebe a mensagem, rotear combina método e caminho, lerJson transforma bytes em dados, criarTarefa aplica a regra, o repositório persiste e responder traduz o resultado para HTTP. Essa ordem é o modelo mental do projeto. Quando algo falha, identifique em qual fronteira o dado deixou de ter a forma esperada, em vez de adicionar console.log aleatoriamente por todos os arquivos.

Uma função pura como criarTarefa pode receber entradas diretas em um teste. Já o teste de rota precisa observar a borda HTTP. Os dois níveis respondem perguntas diferentes e juntos tornam o diagnóstico mais rápido.

js
import { createServer } from 'node:http';

createServer(async (req, res) => {
  try {
    await rotear(req, res);
  } catch (erro) {
    const status = erro.statusCode ?? 500;
    if (status === 500) console.error(erro);
    responder(res, status, { erro: status === 500 ? 'Erro interno' : erro.message });
  }
}).listen(3000, () => console.log('API em http://localhost:3000'));

Teste o percurso completo

Com o servidor em execução, use outro terminal:

bash
curl -i http://localhost:3000/tarefas
curl -i -X POST http://localhost:3000/tarefas \
  -H 'content-type: application/json' \
  -d '{"titulo":"Revisar módulos"}'
curl -i -X POST http://localhost:3000/tarefas \
  -H 'content-type: application/json' \
  -d '{"titulo":""}'

Espere 200, 201 e 400, nessa ordem. Reinicie o processo e consulte de novo: a tarefa precisa continuar no arquivo. O laboratório pratica somente o serviço em memória no navegador; não afirma abrir HTTP nem persistir no disco.

Erro comum: confiar só no caminho feliz

Uma API não está pronta porque uma criação funcionou. Teste JSON quebrado, título vazio, rota ausente e reinicialização. Também não devolva a stack trace ao cliente; ela pode revelar caminhos e detalhes internos. Registre o necessário no servidor e mantenha a resposta pública estável.

Na missão final, o projeto está concluído quando os quatro resultados são demonstráveis: listar gera 200, criar gera 201, entrada inválida gera 400 e dados sobrevivem ao reinício. Depois, uma evolução natural é trocar o arquivo por banco de dados e o roteamento manual por um framework, preservando as mesmas responsabilidades.

Laboratório ao vivo

Serviço de tarefas em memória

O sandbox executa a regra do serviço, sem HTTP ou disco. Adicione, conclua e liste tarefas; depois provoque um título vazio e confirme o erro controlado.

Pronto para testar
Resultado

Pare e pense

Qual responsabilidade deve ficar fora do módulo de regras de tarefas?

Escolha uma resposta
Missão da aula

Faça sem copiar

Construa e teste GET e POST /tarefas. O projeto passa quando POST válido gera 201, título vazio gera 400 e uma nova execução ainda lista os dados gravados.

Fontes para consultar

Terminou a missão?

Marque apenas quando você conseguir explicar o conceito e concluir o desafio. O progresso fica salvo somente neste navegador.

Voltar ao curso e ver seu progresso →