Arquitetura de Integração com a API TheMealDB
A integração de APIs de terceiros é um pilar fundamental no desenvolvimento web moderno para fornecer dados dinâmicos e ricos. A TheMealDB oferece uma interface RESTful gratuita que permite acessar um vasto catálogo de instruções culinárias. Para construir uma aplicação robusta, é necessário mapear os principais pontos de extremidade (endpoints) e estruturar a comunicação assíncrona.
| Funcionalidade | Endpoint | Descrição |
|---|---|---|
| Busca por Termo | /search.php?s={termo} |
Localiza receitas através de palavras-chave. |
| Consulta por ID | /lookup.php?i={id} |
Retorna os detalhes completos de um prato específico. |
| Receita Aleatória | /random.php |
Obtém uma sugestão culinária ao acaso. |
Implementação de Requisições Assíncronas
Utilizamos a Fetch API do JavaScript para realizar as chamadas de rede. Abaixo, encapsulamos a lógica em funções modulares para facilitar a manutenção e o reaproveitamento de código.
// Função para localizar pratos por palavra-chave
async function filtrarReceitas(termoBusca) {
try {
const urlAlvo = `https://www.themealdb.com/api/json/v1/1/search.php?s=${encodeURIComponent(termoBusca)}`;
const conexao = await fetch(urlAlvo);
if (!conexao.ok) throw new Error('Falha na comunicação com o servidor');
const resultado = await conexao.json();
return resultado.meals || [];
} catch (erro) {
console.error('Erro na pesquisa:', erro);
return [];
}
}
// Função para buscar detalhes de um item específico
async function carregarDetalhesPrato(identificador) {
const resposta = await fetch(`https://www.themealdb.com/api/json/v1/1/lookup.php?i=${identificador}`);
const dados = await resposta.json();
return dados.meals ? dados.meals[0] : null;
}
Processamento e Normalização de Dados
Os dados retornados pela TheMealDB possuem uma estrutura peculiar para ingredientes e medidas (espalhados em campos numerados de 1 a 20). É recomendável normalizar esses dados para um formato de array mais amigável ao frontend.
function formatarDadosReceita(dadosBrutos) {
const listaIngredientes = [];
for (let contador = 1; contador <= 20; contador++) {
const ingrediente = dadosBrutos[`strIngredient${contador}`];
const quantidade = dadosBrutos[`strMeasure${contador}`];
if (ingrediente && ingrediente.trim() !== "") {
listaIngredientes.push({
item: ingrediente.trim(),
medida: quantidade ? quantidade.trim() : 'a gosto'
});
}
}
return {
id: dadosBrutos.idMeal,
titulo: dadosBrutos.strMeal,
categoria: dadosBrutos.strCategory,
origem: dadosBrutos.strArea,
passoAPasso: dadosBrutos.strInstructions,
capa: dadosBrutos.strMealThumb,
linkVideo: dadosBrutos.strYoutube,
componentes: listaIngredientes
};
}
Estratégia de Cache e Performance
Para evitar requisições redundantes e melhorar a velocidade de resposta, implementamos um sistema de cache em memória com tempo de expiração (TTL).
class GerenciadorDeCache {
constructor(expiracaoEmMinutos = 15) {
this.repositorio = new Map();
this.tempoVida = expiracaoEmMinutos * 60 * 1000;
}
salvar(chave, conteudo) {
this.repositorio.set(chave, {
valor: conteudo,
criadoEm: Date.now()
});
}
obter(chave) {
const item = this.repositorio.get(chave);
if (!item) return null;
const expirou = (Date.now() - item.criadoEm) > this.tempoVida;
if (expirou) {
this.repositorio.delete(chave);
return null;
}
return item.valor;
}
}
const cacheReceitas = new GerenciadorDeCache(30);
Interface de Usuário e Feedback Visual
O suceso da aplicação depende de como os resultados são apresentados. Implementamos uma grade responsiva e efeitos de interatividade para melhorar a experiência do usuário (UX).
Efeito de Hover nos Cards
Ao passar o mouse sobre uma receita, as informações principais são reveladas através de uma sobreposição suave com CSS.
.container-receitas {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 25px;
padding: 20px;
}
.card-prato {
position: relative;
overflow: hidden;
cursor: pointer;
border: 3px solid #fff;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.card-prato-info {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.75);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease-out;
}
.card-prato:hover .card-prato-info {
opacity: 1;
}
Adaptabilidade e Design Responsivo
A aplicação utiliza Media Queries para garantir que a navegação seja fluida tanto em desktops quanto em dispositivos móveis, ajustando o número de colunas da grade de acordo com a largura do viewport.
/* Ajustes para Tablets */
@media (max-width: 900px) {
.container-receitas {
grid-template-columns: repeat(3, 1fr);
}
}
/* Ajustes para Smartphones */
@media (max-width: 600px) {
.container-receitas {
grid-template-columns: 1fr;
}
.busca-input {
width: 90%;
}
.detalhes-imagem {
width: 100%;
height: auto;
}
}
Tratamento de Estados e Erros
Uma aplicação resiliente deve informar ao usuário o que está acontecendo durante o ciclo de vida da requisição.
- Carregamento: Exibir um spinner ou mensagem enquanto os dados são buscados.
- Sem Resultados: Mostrar um aviso amigável caso a busca não retorne nenhum prato.
- Falha na Rede: Capturar exceções do
fetche sugerir que o usuário verifique sua conexão. - Validação de Input: Impedir envios de formulários vazios com alertas informativos.