Buscando Dados da API
Os cards até agora têm dados escritos diretamente no HTML. Isso não escala: toda vez que alguém cria um evento no TamoJunto, alguém teria que editar o arquivo HTML. O que precisamos é buscar os dados da API construída no capítulo anterior.
Servindo a página pelo backend
Antes de fazer o fetch, tem um detalhe de infraestrutura. Se você abrir o
arquivo HTML com duplo clique, o navegador carrega ele via protocolo file://.
Nesse caso, uma chamada fetch("http://localhost:3000/api/events") vai ser
bloqueada pelo navegador — uma política de segurança chamada CORS (Cross-Origin
Resource Sharing) impede que uma página de uma origem (file://) faça
requisições para outra (http://localhost:3000)1.
A solução mais simples: servir a página HTML pelo próprio backend. Assim, a página e a API estão na mesma origem, e o navegador permite a requisição sem restrições.
Crie um diretório web/ na raiz do projeto e mova o index.html para lá.
Depois, adicione uma linha no app.ts do backend, antes das rotas da API:
import path from "path";
// ... (antes das rotas)
app.use(express.static(path.join(__dirname, "../../web")));
app.use(eventsRoutes);
Código 6-6: Servindo arquivos estáticos do diretório web/ pelo Express.
Agora acesse http://localhost:3000/ no navegador — a página carrega. E como a
página está na mesma origem que a API, o fetch vai funcionar sem problemas.
Renderizando a lista com fetch
Remova os cards estáticos do HTML. Deixe apenas o contêiner vazio:
<body>
<h1>TamoJunto — Eventos</h1>
<div id="event-list"></div>
<script>
async function loadEvents() {
const response = await fetch("/api/events");
const events = await response.json();
const container = document.getElementById("event-list");
for (const ev of events) {
const card = document.createElement("article");
const title = document.createElement("h2");
title.textContent = ev.title;
const date = document.createElement("time");
date.setAttribute("datetime", ev.date);
date.textContent = new Date(ev.date).toLocaleString("pt-BR", {
dateStyle: "short",
timeStyle: "short",
});
const desc = document.createElement("p");
desc.textContent = ev.description;
const location = document.createElement("p");
location.textContent = `📍 ${ev.location}`;
const info = document.createElement("p");
info.textContent =
`Vagas: ${ev.capacity} · Organizado por ${ev.organizer_name}`;
const btn = document.createElement("button");
btn.textContent = "RSVP";
card.append(title, date, desc, location, info, btn);
container.appendChild(card);
}
}
loadEvents();
</script>
</body>
Código 6-7: Renderizando os 12 eventos a partir da API com manipulação direta do DOM.
Recarregue a página. Os 12 eventos aparecem, com dados reais vindos do banco de
dados. Abra o DevTools do navegador (F12), vá na aba “Network” e recarregue:
você verá a requisição GET /api/events e a resposta JSON com os 12 objetos.
O que esse código faz, passo a passo:
fetch("/api/events")dispara uma requisição HTTP GET para a API. Oawaitespera a resposta chegar.response.json()lê o corpo da resposta e parseia o JSON, retornando um array de objetos JavaScript.- Para cada evento, criamos elementos HTML com
document.createElement, preenchemos seus conteúdos comtextContente os montamos dentro do card comappend. - Cada card é adicionado ao contêiner com
appendChild.
A data chega como string ISO ("2026-09-15T11:00:00.000Z") e precisa ser
formatada para o leitor. new Date(ev.date).toLocaleString("pt-BR") converte
para o formato brasileiro:
15/09/2026, 08:00
O que falta
Esse código não trata erros. Se o backend estiver fora do ar, a página fica em branco — nenhuma mensagem, nenhuma indicação de que algo deu errado. Também não mostra nenhum feedback enquanto os dados carregam. Esses problemas existem, mas vamos tolerá-los por agora. O próximo capítulo introduz um framework que torna esses cenários muito mais simples de lidar.
-
CORS é o mecanismo pelo qual o navegador decide se uma página pode fazer requisições para um servidor de outra origem. Veremos CORS em detalhes quando implementarmos autenticação, onde as requisições do frontend precisam enviar cookies para o backend. ↩