Conectando seu HTML a um Servidor

🥑 Usando a API do Rick and Morty

🚀 O que vamos aprender hoje?

  • O que é uma API (A Analogia do Garçom).
  • O que é JSON e como ler os dados que o servidor nos envia.
  • Como usar a função fetch() do JavaScript para "pedir" dados.
  • Como usar o DOM para exibir esses dados dinamicamente no seu HTML.

📋 Pré-requisitos

Para esta aula, você só precisa de um conhecimento básico em:

  • HTML: Entender tags como <div>, <h1>, e <script>.
  • JavaScript:
    • Variáveis (const, let)
    • Funções (function() {})
    • Seleção de elementos (document.getElementById)

1. O que é uma API?

(A Analogia do Garçom)

  • Você (Cliente): Sua página HTML/JS.
  • Cozinha (Servidor): Onde os dados do Rick and Morty vivem.
  • Garçom (API): A interface que busca os dados para você.

Você não vai até a cozinha. Você usa a API (fetch) para pedir ao Servidor pelos dados, e ele os traz para você (em JSON).

2. O que é fetch()?

(O Pedido)

  • fetch() é a função moderna do JavaScript que "chama o garçom".
  • É como você diz: "Ei, JS, vá até esta URL e me traga o que tem lá".
  • fetch() é Assíncrono: Ele não para seu código. Ele faz o pedido e te avisa "quando" a resposta chegar.
  • Usamos .then() (ou async/await) para lidar com essa espera.

3. O que é JSON?

(O Cardápio / A Resposta)

  • O servidor não responde com HTML. Ele responde com JSON (JavaScript Object Notation).
  • É um formato de texto universal para troca de dados.
  • A boa notícia: Ele se parece exatamente com um objeto JavaScript!
{
  "info": { ... },
  "results": [
    {
      "id": 1,
      "name": "Rick Sanchez",
      "status": "Alive",
      "image": "[https://url.da.imagem/1.jpeg](https://url.da.imagem/1.jpeg)"
    },
    { ... }
  ]
}

⚡ Foco: Entendendo o fetch()

Vamos mergulhar fundo na ferramenta mais importante
para "conversar" com servidores.

O Telefone do JavaScript

Pense no fetch() como um telefone.
Uma ligação não é instantânea. Você precisa esperar.

  1. Discar (chamar fetch)
  2. Esperar atender (a Promise fica pending)
  3. Servidor atende (a Promise é fulfilled com uma Response)
  4. Pedir a informação (chamar .json())
  5. Esperar ele buscar (outra Promise)
  6. Ele entrega a informação (os data)

O que é uma Promise (Promessa)?

fetch() não retorna os dados. Ele retorna uma Promise.
É um objeto que representa um valor que ainda não está pronto.

  • pending (Pendente): A "ligação" está chamando.
  • fulfilled (Realizada): Sucesso! O servidor atendeu.
  • rejected (Rejeitada): Falha! A "ligação caiu" (erro de rede).

Usamos .then() para quando ela é realizada.
Usamos .catch() para quando ela é rejeitada.

Anatomia da Requisição (.then())

Vamos dissecar o código:

fetch(API_URL)
    .then(response => {
        // ... Bloco 1: A Resposta HTTP ...
    })
    .then(data => {
        // ... Bloco 2: Os Dados JSON ...
    })
    .catch(error => {
        // ... Bloco 3: O Erro ...
    });

Bloco 1: A Response

O primeiro .then() NÃO recebe os dados. Ele recebe a "capa" da resposta.

.then(response => {
    // 'response' tem o status:
    // response.status (ex: 200, 404, 500)
    // response.ok (true se status 200-299)
    
    // ... aqui verificamos se response.ok ...

    // Se ok, pedimos para ler os dados.
    // Isso retorna OUTRA PROMISSE!
    return response.json(); 
})

⚠️ A Maior "Pegadinha" do fetch()

Um erro 404 (Não Encontrado) ou 500 (Erro de Servidor) NÃO dispara o .catch()!

  • O .catch() só pega erros de rede (sem internet, DNS falhou).
  • Para o fetch, um 404 é uma resposta bem-sucedida (o servidor atendeu e disse "não achei").

Por isso, este bloco é ESSENCIAL:

if (!response.ok) {
    // response.ok é false para 404, 500, etc.
    // Nós manualmente criamos um erro para pular para o .catch()
    throw new Error('HTTP status ' + response.status);
}

Bloco 2: Os Dados (data)

O segundo .then() espera a Promise do response.json() ser resolvida.

.then(data => {
    // AGORA SIM!
    // 'data' é o objeto JSON puro, pronto para usar.
    console.log(data.results);
    exibirPersonagens(data.results);
})

Bloco 3: O Erro (.catch())

Este bloco será executado se:

  1. O fetch() falhar (erro de rede, sem internet).
  2. Nós forçamos um erro no Bloco 1 (o throw new Error).
.catch(error => {
    console.error('Falha na requisição:', error);
});

A Forma Moderna: async / await

.then().then() pode ficar confuso.
async/await é "açúcar sintático" para fazer a mesma coisa de forma mais legível.

async / await - O Código

// 1. Marcamos a função como 'async'
async function buscarPersonagensAsync() {
    // 6. 'try...catch' substitui o .catch()
    try {
        // 2. 'await' espera o fetch (substitui o 1º .then)
        const response = await fetch(API_URL);

        // 3. Verificação de erro (IGUALMENTE IMPORTANTE!)
        if (!response.ok) {
            throw new Error('Erro HTTP: ' + response.status);
        }

        // 4. 'await' espera o .json() (substitui o 2º .then)
        const data = await response.json();

        // 5. Agora temos os dados!
        exibirPersonagens(data.results);
    } catch (error) {
        console.error('Falha ao buscar personagens:', error);
    }
}

Comparativo: .then() vs async/await

Característica Sintaxe com .then() Sintaxe com async/await
Estrutura Aninhada (cadeia de .then()) "Plana", código síncrono
Obtenção de Dados response.json().then(data => ...) const data = await response.json()
Tratamento de Erros .catch(error => ...) try { ... } catch (error) { ... }

Ambos fazem a mesma coisa. async/await é geralmente mais fácil de ler.

🔧 Mãos à Obra!

Agora que entendemos o fetch, vamos montar nossa página.

  1. HTML: O esqueleto.
  2. CSS: A aparência.
  3. JS: A mágica (fetch + DOM).

index.html

O HTML é simples. O mais importante é o <main id="personagens-container">, que é onde o JS vai "desenhar" os cards, e o <script> no final.

<!DOCTYPE html>
<html lang="pt-br">
<head>
    <title>Portal do Rick and Morty</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>Personagens de Rick and Morty</h1>

    <main id="personagens-container" class="container">
        </main>

    <script src="app.js"></script>
</body>
</html>

style.css

Vamos focar no layout dos cards. Usaremos flexbox para organizar.

body {
    background-color: #272B33;
    color: #F5F5F5;
    font-family: sans-serif;
}
.container {
    display: flex;
    flex-wrap: wrap; /* Permite quebrar a linha */
    justify-content: center;
    gap: 20px;
}
.card {
    background-color: #3C3E44;
    border-radius: 10px;
    overflow: hidden;
    width: 280px;
}
.card img {
    width: 100%;
}
.card-info {
    padding: 15px;
}

app.js (Buscando os Dados)

Aqui nós fazemos o fetch e tratamos a resposta.

const API_URL = 'https://rickandmortyapi.com/api/character';

window.addEventListener('DOMContentLoaded', () => {
    buscarPersonagens();
});

function buscarPersonagens() {
    fetch(API_URL)
        .then(response => {
            if (!response.ok) { // Importante: Checagem de erro!
                throw new Error('Erro HTTP: ' + response.status);
            }
            return response.json();
        })
        .then(data => {
            exibirPersonagens(data.results);
        })
        .catch(error => {
            console.error('Falha ao buscar personagens:', error);
        });
}

app.js (Alternativa com async/await)

Veja como o código de busca fica mais "limpo" com async/await.

const API_URL = 'https://rickandmortyapi.com/api/character';

window.addEventListener('DOMContentLoaded', () => {
    buscarPersonagensAsync(); // Chamamos a versão async
});

async function buscarPersonagensAsync() {
    try {
        const response = await fetch(API_URL);
        if (!response.ok) {
            throw new Error('Erro HTTP: ' + response.status);
        }
        const data = await response.json();
        exibirPersonagens(data.results); // Mesma função de antes!
    } catch (error) {
        console.error('Falha ao buscar personagens:', error);
    }
}

app.js (Exibindo no DOM)

Esta função é a mesma para ambas as abordagens (.then ou async).
Ela recebe o array de personagens e cria o HTML.

function exibirPersonagens(personagens) {
    const container = document.getElementById('personagens-container');

    // Usamos 'map' para transformar CADA personagem em uma string HTML
    const htmlPersonagens = personagens.map(personagem => {
        return `
            <article class="card">
                <img src="${personagem.image}" alt="${personagem.name}">
                <div class="card-info">
                    <h3>${personagem.name}</h3>
                    <p>Status: ${personagem.status}</p>
                    <p>Espécie: ${personagem.species}</p>
                </div>
            </article>
        `;
    }).join(''); // Junta todas as strings em uma só

    // Insere o HTML gigante no container de uma vez
    container.innerHTML = htmlPersonagens;
}

🎓 Resumo da Aula

  1. O HTML carrega (vazio).
  2. O JavaScript é executado.
  3. O fetch() "pede" os dados à API (retorna uma Promise).
  4. O await (ou .then()) espera a Response.
  5. Verificamos se response.ok é true.
  6. O await (ou .then()) espera o .json() (outra Promise).
  7. Os data chegam e chamamos exibirPersonagens().
  8. O innerHTML insere os cards na página.

Sua página, antes estática, agora está VIVA com dados de um servidor!

🏆 Próximos Passos (Trabalho valendo 3,0 pontos)

  • Paginação: A API envia os dados em páginas. Como buscar a page=2? (Dica: a API envia um link data.info.next).
  • Barra de Busca: Adicionar um <input> e filtrar os personagens pelo nome.
  • Detalhes: Criar uma página que mostra os detalhes de UM personagem específico ao clicar no card.

Dúvidas?

end list