Paginação em API REST: Um Guia Completo

Rebecca Kovács

Rebecca Kovács

7 junho 2025

Paginação em API REST: Um Guia Completo

No mundo do desenvolvimento de aplicações modernas, as APIs REST servem como a camada de comunicação fundamental, permitindo que sistemas díspares troquem dados de forma transparente. À medida que as aplicações crescem em escala e complexidade, o volume de dados que elas manipulam também aumenta. Solicitar um conjunto de dados inteiro, potencialmente contendo milhões ou até bilhões de registros, em uma única chamada de API é ineficiente, não confiável e um gargalo significativo de desempenho. É aqui que entra uma técnica crucial no design e desenvolvimento de APIs: a paginação de API REST. Este guia oferece uma visão aprofundada e abrangente da implementação de paginação em APIs REST, cobrindo tudo, desde conceitos fundamentais até implementações avançadas no mundo real usando várias pilhas tecnológicas como Node.js, Python e .NET.

💡
Quer uma ótima ferramenta de Teste de API que gera documentação de API bonita?

Quer uma plataforma integrada e completa para sua Equipe de Desenvolvedores trabalhar em conjunto com produtividade máxima?

Apidog atende a todas as suas demandas e substitui o Postman por um preço muito mais acessível!
botão

Os Fundamentos da Paginação de API REST

Antes de mergulhar em exemplos de código complexos e padrões de design, é essencial ter uma compreensão sólida do que é paginação e por que é um aspecto não negociável do design profissional de APIs.

O que é Paginação em APIs REST?

Em sua essência, a paginação de API REST é uma técnica usada para pegar a resposta de um endpoint de API REST e segmentá-la em unidades menores e mais gerenciáveis, frequentemente chamadas de "páginas". Em vez de entregar um conjunto de dados potencialmente massivo de uma só vez, a API retorna um pedaço pequeno e previsível dos dados. Crucialmente, a resposta da API também inclui metadados que permitem que um cliente busque incrementalmente os pedaços subsequentes, caso precise de mais dados.

Este processo é análogo às páginas de um livro ou aos resultados de pesquisa no Google. Você é apresentado à primeira página de resultados, juntamente com controles para navegar para a segunda, terceira e assim por diante. Como comunidades de desenvolvedores como DEV Community e plataformas como Merge.dev apontam, este é o processo de quebrar um grande conjunto de dados em pedaços menores, que podem ser buscados incrementalmente por um cliente se ele realmente quiser todos esses dados. É um conceito fundamental para a construção de aplicações robustas e escaláveis.

Por que a Paginação é um Requisito Essencial no Design Moderno de APIs?

A motivação principal para a paginação é garantir que as respostas da API sejam mais fáceis de lidar tanto para o servidor quanto para o cliente. Sem ela, as aplicações enfrentariam limitações severas e uma experiência de usuário ruim. Os principais benefícios incluem:

Estratégias e Técnicas Comuns de Paginação

Existem várias maneiras de implementar paginação, mas duas estratégias principais se tornaram os padrões de fato na indústria. A escolha entre elas tem implicações significativas para desempenho, consistência de dados e experiência do usuário.

Paginação Baseada em Offset: A Abordagem Fundamental

A paginação baseada em offset, frequentemente chamada de "paginação por número de página", é frequentemente a primeira abordagem que os desenvolvedores aprendem. É conceitualmente simples e vista em muitas aplicações web. Funciona usando dois parâmetros principais:

Uma requisição típica se parece com isto: GET /api/products?limit=25&offset=50

Isso se traduziria em uma consulta SQL como:SQL

SELECT * FROM products ORDER BY created_at DESC LIMIT 25 OFFSET 50;

Esta consulta pula os primeiros 50 produtos e recupera os próximos 25 (ou seja, produtos 51-75).

Prós:

Contras e Limitações:

Paginação Baseada em Cursor (Keyset): A Solução Escalável

A paginação baseada em cursor, também conhecida como keyset ou seek pagination, resolve os problemas de desempenho e consistência do método offset. Em vez de um número de página, ela usa um "cursor", que é um ponteiro estável e opaco para um registro específico no conjunto de dados.

O fluxo é o seguinte:

  1. O cliente faz uma requisição inicial para uma página de dados.
  2. O servidor retorna a página de dados, juntamente com um cursor que aponta para o último item naquele conjunto.
  3. Para a próxima página, o cliente envia esse cursor de volta ao servidor.
  4. O servidor então recupera os registros que vêm depois daquele cursor específico, efetivamente "buscando" aquele ponto no conjunto de dados.

O cursor é tipicamente um valor codificado derivado da(s) coluna(s) usada(s) para ordenação. Por exemplo, se ordenando por created_at (um timestamp), o cursor poderia ser o timestamp do último registro. Para lidar com empates, uma segunda coluna única (como o id do registro) é frequentemente incluída.

Uma requisição usando um cursor se parece com isto: GET /api/products?limit=25&after_cursor=eyJjcmVhdGVkX2F0IjoiMjAyNS0wNi0wN1QxODowMDowMC4wMDBaIiwiaWQiOjg0N30=

Isso se traduziria em uma consulta SQL muito mais performática:SQL

SELECT * FROM products
WHERE (created_at, id) < ('2025-06-07T18:00:00.000Z', 847)
ORDER BY created_at DESC, id DESC
LIMIT 25;

Esta consulta usa um índice em (created_at, id) para "buscar" instantaneamente o ponto de partida correto, evitando uma varredura completa da tabela e tornando-a consistentemente rápida, independentemente da profundidade da paginação do usuário.

Prós:

Contras:

Uma Comparação dos Dois Principais Tipos de Paginação

A escolha entre paginação por offset e por cursor depende inteiramente do caso de uso.

CaracterísticaPaginação por OffsetPaginação por Cursor
DesempenhoRuim para páginas profundas em grandes conjuntos de dados.Excelente e consistente em qualquer profundidade.
Consistência de DadosPropenso a perder/repetir dados (page drift).Alta; novos dados não afetam a paginação.
NavegaçãoPode pular para qualquer página.Limitado a páginas próxima/anterior.
ImplementaçãoSimples e direta.Mais complexa; requer lógica de cursor.
Caso de Uso IdealPequenos conjuntos de dados estáticos; UIs de administração.Feeds de rolagem infinita; grandes conjuntos de dados dinâmicos.

Melhores Práticas de Implementação para Paginação do Lado do Servidor

Independentemente da estratégia escolhida, aderir a um conjunto de melhores práticas resultará em uma API limpa, previsível e fácil de usar. Esta é frequentemente uma parte essencial da resposta para "Qual é a melhor prática de paginação do lado do servidor?".

Projetando o Payload de Resposta da Paginação

Um erro comum é retornar apenas um array de resultados. Um payload de resposta de paginação bem projetado deve ser um objeto que "envelopa" os dados e inclui metadados de paginação claros.JSON

{
  "data": [
    { "id": 101, "name": "Product A" },
    { "id": 102, "name": "Product B" }
  ],
  "pagination": {
    "next_cursor": "eJjcmVhdGVkX2F0Ij...",
    "has_next_page": true
  }
}

Para paginação por offset, os metadados seriam diferentes:JSON

{
  "data": [
    // ... results
  ],
  "metadata": {
    "total_results": 8452,
    "total_pages": 339,
    "current_page": 3,
    "per_page": 25
  }
}

Esta estrutura torna trivial para o cliente saber se há mais dados para buscar ou para renderizar controles de UI.

Um princípio central do REST é HATEOAS (Hypermedia as the Engine of Application State). Isso significa que a API deve fornecer aos clientes links para navegar para outros recursos ou ações. Para paginação, isso é incrivelmente poderoso. Como demonstrado na Documentação do GitHub, uma maneira padronizada de fazer isso é com o cabeçalho HTTP Link.

Link: <https://api.example.com/items?page=3>; rel="next", <https://api.example.com/items?page=1>; rel="prev"

Alternativamente, esses links podem ser colocados diretamente no corpo da resposta JSON, o que é frequentemente mais fácil para clientes JavaScript consumirem:JSON

"pagination": {
  "links": {
    "next": "https://api.example.com/items?limit=25&offset=75",
    "previous": "https://api.example.com/items?limit=25&offset=25"
  }
}

Isso libera o cliente de ter que construir URLs manualmente.

Permitindo que Clientes Controlem o Tamanho da Página

É uma boa prática permitir que os clientes solicitem páginas adicionais de resultados para respostas paginadas e também que alterem o número de resultados retornados em cada página. Isso é tipicamente feito com um parâmetro de query limit ou per_page. No entanto, o servidor sempre deve impor um limite máximo razoável (por exemplo, 100) para evitar que clientes solicitem muitos dados de uma vez e sobrecarreguem o sistema.

Combinando Paginação com Filtragem e Ordenação

APIs do mundo real raramente apenas paginam; elas também precisam suportar filtragem e ordenação. Como mostrado em tutoriais cobrindo tecnologias como .NET, adicionar esses recursos é um requisito comum.

Uma requisição complexa pode se parecer com isto: GET /api/products?status=published&sort=-created_at&limit=50&page=2

Ao implementar isso, é crucial que os parâmetros de filtragem e ordenação sejam considerados parte da lógica de paginação. A ordem de sort deve ser estável e determinística para que a paginação funcione corretamente. Se a ordem de ordenação não for única, você deve adicionar uma coluna de desempate única (como o id) para garantir uma ordenação consistente entre as páginas.

Exemplos de Implementação no Mundo Real

Vamos explorar como implementar esses conceitos em vários frameworks populares.

Paginação de API REST em Python com Django REST Framework

Uma das combinações mais populares para construir APIs é Python com o Django REST Framework (DRF). O DRF oferece suporte poderoso e integrado para paginação, tornando incrivelmente fácil começar. Ele oferece classes para diferentes estratégias:

Você pode configurar um estilo de paginação padrão globalmente e então simplesmente usar um ListAPIView genérico, e o DRF cuida do resto. Este é um excelente exemplo de paginação de API REST em Python.Python

# In your settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
    'PAGE_SIZE': 50
}

# In your views.py
class ProductListView(generics.ListAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    # DRF handles the entire pagination logic automatically!

Construindo uma API REST Paginada com Node.js, Express e TypeScript

No ecossistema Node.js, você frequentemente constrói a lógica de paginação manualmente, o que lhe dá controle total. Esta seção do guia fornece uma visão conceitual da construção de paginação com Node.js, Express e TypeScript.

Aqui está um exemplo simplificado de implementação de paginação por cursor:TypeScript

// In your Express controller
app.get('/products', async (req: Request, res: Response) => {
  const limit = parseInt(req.query.limit as string) || 25;
  const cursor = req.query.cursor as string;

  let query = db.selectFrom('products').orderBy('createdAt', 'desc').orderBy('id', 'desc').limit(limit);

  if (cursor) {
    const { createdAt, id } = JSON.parse(Buffer.from(cursor, 'base64').toString('ascii'));
    // Add the WHERE clause for the cursor
    query = query.where('createdAt', '<=', createdAt).where('id', '<', id);
  }

  const products = await query.execute();

  const nextCursor = products.length > 0
    ? Buffer.from(JSON.stringify({
        createdAt: products[products.length - 1].createdAt,
        id: products[products.length - 1].id
      })).toString('base64')
    : null;

  res.json({
    data: products,
    pagination: { next_cursor: nextCursor }
  });
});

Paginação em um Ecossistema Java ou .NET

Frameworks em outros ecossistemas também fornecem suporte robusto para paginação.

Um Caso de Uso do Mundo Real: Paginando uma API de Catálogo de Produtos

Considere um site de e-commerce com uma "API de Catálogo de Produtos". Este é um caso de uso do mundo real perfeito. O catálogo é grande e dinâmico, com novos produtos sendo adicionados frequentemente.

Tópicos Avançados e Problemas Comuns

Como desenvolvedores no Stack Overflow e Reddit frequentemente descobrem, construir um sistema de paginação verdadeiramente robusto requer lidar com muitos detalhes e casos extremos.

Como Garantir a Consistência de Dados em uma API Paginada

Este é um dos tópicos avançados mais críticos. Como discutido, a única maneira confiável de garantir a consistência de dados em um sistema com escritas frequentes é usar paginação keyset/cursor. Seu design inerentemente impede o page drift. Se por algum motivo você estiver preso à paginação por offset, existem algumas soluções complexas, como criar um snapshot temporário e imutável dos IDs para o conjunto completo de resultados e paginar por essa lista, mas isso é altamente stateful e geralmente não recomendado para APIs REST.

Lidando com Casos Extremos Estranhos

Uma API pronta para produção deve lidar graciosamente com entradas inválidas. Considere estes casos extremos comuns:

Implementação do Lado do Cliente

O lado do cliente é onde a lógica de paginação é consumida. Usar JavaScript para buscar dados paginados de uma API REST como um profissional envolve ler os metadados de paginação e usá-los para fazer requisições subsequentes.

Aqui está um exemplo simples de fetch para um botão "Carregar Mais" usando paginação por cursor:JavaScript

const loadMoreButton = document.getElementById('load-more');
let nextCursor = null; // Store the cursor globally or in component state

async function fetchProducts(cursor) {
  const url = cursor ? `/api/products?cursor=${cursor}` : '/api/products';
  const response = await fetch(url);
  const data = await response.json();

  // ... render the new products ...
  nextCursor = data.pagination.next_cursor;

  if (!nextCursor) {
    loadMoreButton.disabled = true; // No more pages
  }
}

loadMoreButton.addEventListener('click', () => fetchProducts(nextCursor));

// Initial load
fetchProducts(null);

O Futuro da Recuperação de Dados de API e Padrões de Paginação

Embora o REST tenha sido dominante por anos, o cenário está sempre evoluindo.

Evoluindo os Padrões de Paginação de API REST

Não existe um único RFC formal que defina padrões de paginação de API REST. No entanto, um conjunto de convenções fortes surgiu, impulsionado pelas APIs públicas de grandes empresas de tecnologia como GitHub, Stripe e Atlassian. Essas convenções, como usar o cabeçalho Link e fornecer metadados claros, tornaram-se o padrão de fato. A consistência é fundamental; uma plataforma de API bem projetada usará a mesma estratégia de paginação em todos os seus endpoints baseados em lista.

O Impacto do GraphQL na Paginação

O GraphQL apresenta um paradigma diferente. Em vez de múltiplos endpoints, ele tem um único endpoint onde os clientes enviam queries complexas especificando os dados exatos que precisam. No entanto, a necessidade de paginar grandes listas de dados não desaparece. A comunidade GraphQL também padronizou a paginação baseada em cursor através de uma especificação formal chamada Relay Cursor Connections Spec. Isso define uma estrutura precisa para paginar dados, usando conceitos como first, after, last e before para fornecer paginação robusta para frente e para trás.

Conclusão: Um Resumo das Melhores Práticas de Paginação

Dominar a paginação de API REST é uma habilidade crítica para qualquer desenvolvedor backend. É uma técnica essencial para construir aplicações escaláveis, performáticas e amigáveis ao usuário.

Para resumir as melhores práticas de paginação de API REST:

  1. Sempre Paginar: Nunca retorne uma lista ilimitada de resultados de um endpoint de API.
  2. Escolha a Estratégia Certa: Use paginação por offset simples para conjuntos de dados pequenos, não críticos ou estáticos. Para qualquer coisa grande, dinâmica ou voltada para o usuário, prefira fortemente a paginação baseada em cursor por seu desempenho superior e consistência de dados.
  3. Forneça Metadados Claros: Seu payload de resposta sempre deve incluir informações que digam ao cliente como obter a próxima página de dados, seja um next_cursor ou números de página e links.
  4. Use Hipermídia: Use o cabeçalho Link ou links dentro do seu corpo JSON para tornar sua API mais descobrível e fácil de usar.
  5. Lide com Erros Graciosamente: Valide todos os parâmetros de paginação e retorne erros 400 Bad Request claros para entrada inválida.

Seguindo este guia e internalizando estes princípios, você pode projetar e construir APIs REST profissionais, prontas para produção, que podem escalar efetivamente para atender a qualquer demanda.

FAQs sobre Paginação de API REST

1. Qual é a principal diferença entre paginação por offset e por cursor?

A principal diferença reside em como elas determinam qual conjunto de dados recuperar. A paginação por offset usa um offset numérico (como "pular os primeiros 50 itens") para encontrar a próxima página. Isso pode ser lento para grandes conjuntos de dados porque o banco de dados ainda precisa contar os itens que estão sendo pulados. A paginação por cursor usa um ponteiro estável ou "cursor" que aponta para um registro específico (como "obter itens após o produto ID 857"). Isso é muito mais eficiente porque o banco de dados pode usar um índice para pular diretamente para aquele registro.

2. Quando é apropriado usar paginação por offset em vez de paginação por cursor?

A paginação por offset é apropriada para conjuntos de dados que são pequenos, não críticos em termos de desempenho ou que não mudam frequentemente. Sua principal vantagem é a simplicidade e a capacidade de um usuário pular para qualquer número de página específico (por exemplo, "Ir para a Página 10"). Isso a torna adequada para coisas como painéis de administração ou ferramentas internas onde a experiência do usuário de pular entre páginas é mais importante do que lidar com mudanças de dados em tempo real.

3. Como a paginação baseada em cursor impede o problema de pular ou repetir itens?

A paginação baseada em cursor impede a inconsistência de dados porque ela ancora a próxima requisição a um item específico, e não a uma posição numérica. Por exemplo, se você solicitar a página após o item com ID=100, não importa se novos itens são adicionados antes dele; a query sempre começará a buscar a partir do local correto. Com a paginação por offset, se um novo item for adicionado à página 1 enquanto você a visualiza, quando você solicitar a página 2, o último item da página 1 agora será o primeiro item na página 2, causando uma repetição.

4. Existe um padrão oficial para respostas de paginação de API REST?

Não existe um único RFC oficial ou padrão formal que dite como toda paginação de API REST deve ser implementada. No entanto, convenções e melhores práticas fortes surgiram da indústria, em grande parte estabelecidas por grandes APIs públicas como as do GitHub e Stripe. Essas convenções incluem o uso de um cabeçalho HTTP Link com atributos rel="next" e rel="prev", ou a incorporação de um objeto pagination com metadados claros e links diretamente no corpo da resposta JSON.

5. Como devo lidar com ordenação e filtragem em meus endpoints paginados?

Ordenação e filtragem devem ser aplicadas antes da paginação. Os resultados paginados devem ser uma "visão" do conjunto de dados já ordenado e filtrado. É crucial que a ordem de ordenação seja estável e determinística. Se um usuário ordenar por um campo não único (como uma data), você deve adicionar uma chave de ordenação secundária única (como o id de um registro) para atuar como desempate. Isso garante que a ordem dos itens seja sempre a mesma, o que é essencial para que tanto a paginação por offset quanto a por cursor funcionem corretamente.

💡
Quer uma ótima ferramenta de Teste de API que gera documentação de API bonita?

Quer uma plataforma integrada e completa para sua Equipe de Desenvolvedores trabalhar em conjunto com produtividade máxima?

Apidog atende a todas as suas demandas e substitui o Postman por um preço muito mais acessível!
botão

Pratique o design de API no Apidog

Descubra uma forma mais fácil de construir e usar APIs