Guia para Evitar Overfetch e Underfetch em Projetos
1. Introdução
No desenvolvimento de aplicações que consomem APIs, problemas de overfetch e underfetch são comuns e afetam diretamente:
- Performance (tempo de resposta maior, uso desnecessário de rede);
- Escalabilidade (mais carga no servidor e no cliente);
- Manutenção (código complexo e difícil de evoluir);
- Experiência do usuário (telas lentas ou inconsistentes).
Um caso crítico a ser evitado é a necessidade de várias requisições para completar uma única ação, como buscar dados já conhecidos apenas para poder enviá-los em outra chamada.
<u>Este documento define boas práticas para evitar esses problemas.</u>
2. Definições
Overfetch
Quando a aplicação recebe mais dados do que precisa em uma requisição.
Exemplo:
- Endpoint
/users/123retorna nome, e-mail, telefone, endereço, lista de pedidos, amigos etc. - A tela só precisa de nome e foto.
Impactos:
- Maior tráfego de rede;
- Maior tempo de processamento;
- Exposição de dados desnecessários.
Underfetch
Quando a aplicação recebe menos dados do que precisa, obrigando o frontend a fazer múltiplas chamadas para compor a mesma tela.
Exemplo:
/users/123→ retorna só os dados básicos do usuário;/users/123/orders→ retorna pedidos;/users/123/friends→ retorna amigos;- Resultado: várias chamadas só para exibir um perfil.
Impactos:
- Muitas requisições encadeadas;
- Latência acumulada;
- Lógica de montagem mais complexa no frontend.
Underfetch por Requisição Desnecessária
Situação em que o frontend precisa de uma informação já conhecida ou disponível no contexto (como o ID do usuário logado), mas mesmo assim faz uma chamada extra só para buscá-la.
Exemplo ruim:
GET /me→ busca o usuário logado para obter o id;PUT /article/456→ com{updatedBy: userId};
Melhor prática:
- O id do usuário já deve estar disponível no token JWT ou no contexto da sessão.
- Assim, o frontend já tem esse dado em memória e consegue enviar direto
3. Boas Práticas
3.1 Planejamento de Contratos (API Contracts)
- Definir em conjunto frontend + backend quais dados cada tela precisa.
- Evitar endpoints genéricos que retornam "tudo".
- Criar DTOs (Data Transfer Objects) específicos para cada caso de uso.
3.2 Padronização de Endpoints
- Evitar overfetch criando endpoints otimizados:
/users/123/summary→ retorna só dados resumidos.
- Evitar underfetch criando endpoints compostos:
/users/123/profile→ retorna dados básicos, amigos e pedidos em uma única resposta.
3.4 Versionamento e Evolução da API
- Se uma tela precisar de novos dados, não adicionar campos desnecessários a endpoints existentes.
- Criar novas rotas/queries ou versões da API para manter o contrato limpo.
3.5 Monitoramento e Revisão Contínua
- Usar logs de requisições para detectar payloads muito grandes (sinais de overfetch).
- Monitorar quantidade de requests por tela (sinais de underfetch).
- Realizar revisões periódicas no contrato de API.
4. Design de API para Alteração de Status
Em sistemas que trabalham com entidades que possuem status mutáveis (ex.: pedidos, tarefas, processos), é comum a necessidade de alterar esses estados via API.
Existem duas formas principais de modelar essas alterações:
- Rota genérica com envio de status (enum)
- Rotas específicas para cada ação de transição
Essa seção explica a diferença entre elas, destacando vantagens, desvantagens e quando utilizar cada abordagem.
1. Abordagem 1 – Rota Genérica (com Enum)
Exemplo
PUT /orders/{id}/status
{
"status": "SHIPPED"
}
Vantagens
- Extensível → novos status podem ser adicionados sem criar novas rotas.
- Contrato único → um endpoint centralizado para todas as alterações.
- Mais limpo → reduz quantidade de endpoints e facilita documentação.
- Frontend simples → só precisa enviar o valor do status.
Desvantagens
- Backend precisa validar se a transição é permitida (ex.: não pode voltar de
DELIVERED→PENDING). - Regras de negócio mais complexas devem ser tratadas na lógica do backend.
2. Abordagem 2 – Rotas Específicas por Ação
Exemplo
POST /orders/{id}/separate
POST /orders/{id}/ship
POST /orders/{id}/deliver
Vantagens
- Semântica clara → a ação já fica explícita na URL.
- Validação embutida → cada rota representa apenas transições válidas.
- Bom para workflows complexos, em que cada status exige processos diferentes (ex.: integração com transportadora, emissão de nota fiscal etc.).
Desvantagens
- Pouco escalável → cada novo status exige uma nova rota.
- Mais manutenção → aumenta a complexidade da API e da documentação.
- Frontend rígido → precisa conhecer cada rota específica.
3. Qual escolher?
- Workflow simples (fluxo previsível de status)
Usar rota genérica com enums.
Exemplo de fluxo:PENDING → SEPARATED → SHIPPED → DELIVERED. - Workflow complexo (transições específicas com regras diferentes)
Usar rotas específicas por ação.
Exemplo: mudança de status que dispara side-effects distintos (envio de notificações, geração de documentos fiscais, integrações externas etc.).
4. Resumo
<colgroup><col></col><col></col><col></col></colgroup> Rota Genérica (Enum)
Rotas Específicas
Escalabilidade
Alta (fácil adicionar novos status)
Baixa (cada status precisa de rota)
Clareza Semântica
Média (precisa validar transições)
Alta (ação explícita na URL)
Manutenção
Mais simples
Mais complexa
Casos ideais
Workflows simples e previsíveis
Workflows complexos, com side-effects diferentes