Overfetching é o problema de API em que respostas carregam mais dados do que o cliente precisa; underfetching força requisições extras para obter o bastante. São os modos de falha gêmeos dos formatos de resposta fixos — um desperdiça bytes, o outro desperdiça round trips — e a maioria das APIs comete os dois na mesma tela: cada resposta gorda demais, e respostas demais.
Principais pontos
| Pergunta | Resposta |
|---|---|
| Overfetching | Demais por resposta — banda, parsing, bateria, exposição |
| Underfetching | De menos por resposta — round trips extras, cascatas, N+1 |
| A causa raiz | Formatos fixos de endpoint encontrando telas com necessidades diferentes |
| Soluções REST | Sparse fieldsets · params include/expand · paginação · endpoints compostos |
| A resposta do GraphQL | Selection sets — com ressalvas honestas na camada de resolvers |
Uma tela, três formas de buscá-la
Uma lista de posts que renderiza cada título com o nome do autor:
Overfetching Underfetching
GET /posts GET /posts (só IDs de autor)
→ 20 posts × 40 campos GET /users/11 ┐
→ ~160 KB enviados, GET /users/12 │ mais 20 chamadas —
~6 KB renderizados (96% desperdício) … │ a cascata N+1
GET /users/30 ┘
A query moldada
GET /posts?fields=title,summary,author&include=author&limit=20
→ 20 posts × 3 campos + seus autores — um round trip, ~7 KB
A mesma query moldada como código de SDK — projeção e relação em uma requisição:
// JavaScript / Node.js — Back4app JS SDK
// One shaped query: no overfetch, no underfetch
const query = new Parse.Query('Post');
query.select('title', 'summary', 'author'); // only what the screen renders
query.include('author'); // related object, same response
query.limit(20); // bounded page
const posts = await query.find();
// 1 round trip — not 1 list call + 20 author calls (N+1) // Flutter / Dart — Back4app Flutter SDK
// One shaped query: no overfetch, no underfetch
final query = QueryBuilder<ParseObject>(ParseObject('Post'))
..keysToReturn(['title', 'summary', 'author']) // only what the screen renders
..includeObject(['author']) // related object, same response
..setLimit(20); // bounded page
final response = await query.query();
// 1 round trip — not 1 list call + 20 author calls (N+1) // iOS / Swift — Back4app Swift SDK
// One shaped query: no overfetch, no underfetch
let query = Post.query()
.select("title", "summary", "author") // only what the screen renders
.include("author") // related object, same response
.limit(20) // bounded page
query.find { result in
// 1 round trip — not 1 list call + 20 author calls (N+1)
if case .success(let posts) = result { render(posts) }
} // Android / Kotlin — Back4app Android SDK
// One shaped query: no overfetch, no underfetch
val query = ParseQuery.getQuery<ParseObject>("Post")
query.selectKeys(listOf("title", "summary", "author")) // only what the screen renders
query.include("author") // related object, same response
query.limit = 20 // bounded page
query.findInBackground { posts, e -> if (e == null) render(posts) }
// 1 round trip — not 1 list call + 20 author calls (N+1) O que é overfetching?
Overfetching é a versão de camada de API do SELECT *: o endpoint retorna sua representação fixa completa, independentemente do que o chamador renderiza. Os custos se empilham em camadas. O servidor serializa campos que ninguém lê; a rede os carrega — e é aí que o mobile sofre, já que o tempo de transferência escala com bytes sobre banda limitada e cada kilobyte desnecessário gasta franquia de dados e bateria de rádio; depois o cliente parseia tudo, porque a descompressão acontece antes da renderização e uma resposta inchada é trabalho de parsing inchado mesmo quando a compressão a escondeu na rede.
O custo mais silencioso é a exposição. Um campo de resposta que a UI nunca mostra continua a um clique das ferramentas de desenvolvedor de ser lido — flags internas, e-mails de outros usuários, dados de margem. O OWASP API Security Top 10 rastreia isso como exposição excessiva de dados (broken object property level authorization): o menor privilégio se aplica a corpos de resposta, e um campo que nenhum cliente deveria ver não deveria nem ser serializado.
O que é underfetching?
Underfetching é a deficiência oposta: o formato fixo do endpoint carrega de menos, então o cliente vira um integrador — busca a lista, depois o autor de cada item, depois talvez o avatar de cada autor. Cada chamada extra é um round trip completo, e round trips são a moeda de que as redes móveis são mais pobres: a uns realistas 100 ms por requisição, uma lista de 20 itens resolvida sequencialmente gasta dois segundos só de latência, antes de qualquer conta de payload.
Em escala, essa cascata tem nome — o problema de requisições N+1: uma chamada para N itens, N chamadas para seus detalhes. O formato é fractal; recorre onde quer que uma interface fixa encontre dados relacionais — clientes HTTP contra endpoints REST, resolvers GraphQL contra o banco de dados, ORMs abrindo caminho com lazy loading dentro de um loop — e a cura é sempre alguma forma de agrupar os N em um.
Overfetching vs. underfetching
| Overfetching | Underfetching | |
|---|---|---|
| Sintoma | Respostas cheias de campos não renderizados | Telas montadas a partir de muitas chamadas |
| Unidade de desperdício | Bytes (e tempo de parsing) | Round trips (e latência) |
| Pior em | Redes de franquia, lentas, limitadas por bateria | Redes de alta latência — cascatas se compõem |
| Detecção | Compare campos retornados vs. campos renderizados | Conte requisições por tela na aba de rede |
| Solução direta | Sparse fieldsets / projeção | Params de expansão, endpoints compostos |
| Escalada | Exposição excessiva de dados (segurança) | Tempestades de requisições N+1 (escala) |
O diagnóstico é misericordiosamente mecânico, e nenhum explainer de ranking diz isso: abra a aba de rede em uma tela. Muitas requisições para uma view é underfetching; respostas grandes cujos campos você não encontra na UI é overfetching. Analytics de endpoint generalizam a auditoria — tamanho de payload p95 por endpoint, requisições por sessão por tela.
Corrigindo os dois sem sair do REST
A migração para GraphQL não é o primeiro recurso; convenções REST maduras cobrem a maior parte da distância:
- Sparse fieldsets — um parâmetro
fieldsque projeta a representação: padronizado como sparse fieldsets do JSON:API, espelhado por opções de query no estilo$selecte pelos buildersselect()dos SDKs. A solução do overfetching na origem. - Parâmetros de expansão —
include=author,commentsembute recursos relacionados na mesma resposta (documentos compostos), convertendo uma cascata N+1 em uma requisição. A solução do underfetching na origem. - Paginação — limita a dimensão de lista do overfetching; coleções sem limite são bugs de payload que crescem com a adoção.
- Endpoints sob medida e compostos — quando uma tela sempre precisa do mesmo agregado, dê a ela um endpoint que retorna exatamente esse agregado, montado no servidor, onde a latência entre serviços é de microssegundos, não de round trips móveis.
- Um backend-for-frontend — a versão arquitetural do mesmo movimento: uma camada fina por cliente que fala com APIs internas generosas e serve a cada frontend exatamente o seu formato (o padrão BFF de Sam Newman).
- Compressão — honesto último lugar: encolhe a transmissão, não o desperdício; custo de parsing e exposição sobrevivem intactos.
O GraphQL resolve o problema?
Em grande parte — e o “em grande parte” vale a pena conhecer. Selection sets tornam a lista de campos do cliente a própria requisição, o que aposenta o overfetching clássico, e queries aninhadas montam dados relacionados em um round trip, o que aposenta o underfetching clássico. É exatamente por isso que o debate GraphQL vs. REST começa com essas duas palavras.
As ressalvas moram uma camada abaixo. Clientes que copiam e colam queries generosas overfetcham por hábito — nada garante que uma query casa com o que um componente renderiza, a menos que o time adote fragments por componente. E uma cadeia ingênua de resolvers underfetcha contra o banco de dados: uma query de 20 posts com autores vira 1 + 20 leituras no banco, a menos que os resolvers agrupem através de uma camada de loading — o mesmo N+1, realocado. O GraphQL move o problema para uma camada que você controla, o que é progresso genuíno; ele não o apaga.
Casos de uso comuns
Onde os dois problemas (e suas soluções) aparecem primeiro:
- Telas de lista no mobile — o overfetch canônico: linhas completas enviadas para renderizar três campos por célula.
- Telas de detalhe com relações — post + autor + comentários: cascatas de underfetch, a menos que expandidas ou compostas.
- Mercados de rede lenta — os dois problemas taxados na alíquota máxima; queries moldadas como acessibilidade.
- Dashboards — telas agregadas que ou overfetcham linhas brutas ou underfetcham através de cinco serviços; território de BFF.
- APIs públicas com consumidores diversos — um formato fixo não serve um mostrador de relógio e um console admin; parâmetros de projeção e expansão deixam cada chamador ajustar.
Qual problema você tem? Matriz de decisão
| Evidência na aba de rede | Diagnóstico | Primeira solução |
|---|---|---|
| Uma requisição, resposta grande, poucos campos renderizados | Overfetching | Sparse fieldsets / select() |
| Muitas requisições sequenciais por tela | Underfetching | include / params de expansão |
| Requisições escalam com o tamanho da lista | N+1 | Batch: expansão ou endpoint composto |
| Grandes e muitas ao mesmo tempo | Os dois — comum | Query moldada ou seleção GraphQL |
| Campos de resposta que você preferiria que não saíssem do servidor | Exposição | Apare a serialização no servidor, não no cliente |
Limitações e trade-offs
- Projeção acopla clientes a listas de campos. Um param
fieldsque deriva da UI causa bugs de dados faltantes; tipos gerados e review mantêm as seleções honestas. - Expansão pode corrigir demais.
include=commentsem uma lista quente pode enviar megabytes de relações embutidas — respostas expandidas precisam de paginação e limites de profundidade próprios. - Endpoints sob medida se multiplicam. Endpoints por tela consertam a busca e criam uma conta de manutenção de proliferação de endpoints; BFFs concentram essa proliferação em uma camada com dono, ao custo de operá-la.
- Flexibilidade no servidor tem preço. Projeção e expansão arbitrárias complicam o cache (cada formato é uma chave de cache) e a autorização (toda combinação precisa ser segura de servir).
- Os problemas também são sinais de modelagem. Uma tela que precisa de cortes profundos ou cinco includes pode estar dizendo que os formatos dos recursos estão errados — às vezes a solução é o modelo de dados, não a busca.
Overfetching e underfetching no Back4app
O Back4app é uma plataforma open-source de Backend as a Service (BaaS) que combina banco de dados gerenciado, APIs REST e GraphQL geradas automaticamente, autenticação, armazenamento de arquivos e funções serverless com Cloud Code. As duas soluções vêm como primitivos de query em todos os SDKs — as abas de código acima são o padrão inteiro: select() é o sparse fieldset, include() é o parâmetro de expansão, e juntos transformam uma cascata 1 + N em um round trip moldado contra a API REST gerada automaticamente. Quando os clientes querem controle total do formato da resposta, os mesmos dados são consultáveis via selection sets do GraphQL; quando uma tela precisa de um agregado no servidor, uma função de Cloud Code é um endpoint composto que você escreve em um arquivo. A busca casa com a tela — por idioma, não por redesenho de endpoints.
Perguntas frequentes
O que é overfetching?
Uma API retorna mais dados do que o cliente precisa para a tarefa em questão — uma tela de perfil que renderiza três campos recebe quarenta. O desperdício é pago quatro vezes: serialização no servidor, transferência na rede, parsing no cliente e, no mobile, bateria e dados de franquia. Também pode expor campos que nenhum cliente deveria ver.
O que é underfetching?
Um único endpoint não retorna dados suficientes para renderizar a tela, então o cliente faz requisições adicionais para montá-la. Cada chamada extra é um round trip completo de rede; quando as chamadas são sequenciais — buscar a lista, depois buscar os detalhes item a item — a latência se compõe no problema de requisições N+1.
Qual é a diferença entre overfetching e underfetching?
A direção. Overfetching significa que cada resposta carrega demais — o custo é bytes; underfetching significa que cada resposta carrega de menos — o custo é round trips. Os dois crescem da mesma raiz: formatos de resposta fixos, desenhados uma vez, consumidos por telas com necessidades diferentes. Muitas APIs cometem os dois ao mesmo tempo, na mesma tela.
O GraphQL resolve overfetching e underfetching?
Em grande parte, na camada HTTP: selection sets buscam só os campos pedidos e queries aninhadas reúnem dados relacionados em uma requisição. Mas não é automático — clientes que pedem conjuntos generosos de campos recriam o overfetching, e resolvers ingênuos recriam o underfetching contra o banco de dados como N+1 de resolver, que os loaders com batching existem para corrigir.
Como evitar overfetching em uma API REST?
Sparse fieldsets são a solução direta: um parâmetro fields (ou select() nos query builders dos SDKs) que projeta só as colunas que a tela renderiza. Paginação limita o tamanho das listas, endpoints sob medida casam respostas com telas reais e compressão encolhe o que sobra — embora comprimir inchaço seja mitigação, não cura.
Como corrigir underfetching sem migrar para GraphQL?
Parâmetros de expansão — include ou expand — que embutem objetos relacionados em uma resposta; documentos compostos que enviam um recurso com suas associações; endpoints compostos que agregam as necessidades de uma tela no servidor; e, arquiteturalmente, uma camada backend-for-frontend que faz a montagem perto dos dados, e não através de uma rede móvel.
Como o problema N+1 se relaciona com underfetching?
N+1 é underfetching em escala: uma requisição para uma lista de N itens, depois N requisições de acompanhamento para os detalhes de cada um. O mesmo formato recorre em toda camada — clientes HTTP contra endpoints REST, resolvers GraphQL contra o banco, ORMs com lazy loading de relações — e a solução é sempre a mesma ideia: agrupar os N em um.
Por que overfetching é um risco de segurança?
Campos que uma tela nunca renderiza ainda cruzam a rede — e qualquer pessoa pode abrir as ferramentas de desenvolvedor e lê-los. Flags internas, endereços de e-mail e dados de custo vazam assim; taxonomias de segurança classificam isso como exposição excessiva de dados, e o princípio do menor privilégio se aplica a corpos de resposta tanto quanto a permissões.