Skip to main content

Overview

The API implements cursor-based pagination for endpoints that return large lists of data. This ensures efficient data retrieval and better performance.

Query Parameters

Paginated endpoints accept the following query parameters:
ParameterTypeDefaultMaximumDescription
pageinteger1-The page number to retrieve
limitinteger20100Number of items per page

Example Request

GET /api/routes/:routeId/reviews?page=2&limit=10
fetch('https://api.losinmaduros.com/api/routes/ruta-1/reviews?page=2&limit=10')
  .then(res => res.json())
  .then(data => console.log(data));

Pagination Response Structure

Paginated endpoints return a standardized response format:
{
  "success": true,
  "data": [
    {
      "id": "cm123abc",
      "rating": 5,
      "comment": "Great route!",
      "createdAt": "2026-03-01T10:00:00.000Z",
      "user": {
        "id": "usr_123",
        "name": "John Doe",
        "imageUrl": "https://example.com/avatar.jpg"
      }
    }
  ],
  "pagination": {
    "page": 2,
    "limit": 10,
    "totalCount": 45,
    "totalPages": 5,
    "hasNextPage": true,
    "hasPreviousPage": true
  }
}

Pagination Metadata

The pagination object provides complete information about the current page:
FieldTypeDescription
pageintegerCurrent page number
limitintegerItems per page
totalCountintegerTotal number of items across all pages
totalPagesintegerTotal number of pages available
hasNextPagebooleanWhether there are more pages after the current one
hasPreviousPagebooleanWhether there are pages before the current one

Implementation Details

The pagination logic is implemented in service layer methods. Here’s how it works:
// From reviews.service.ts:13-17
const currentPage = page || 1;
const currentLimit = Math.min(limit || 20, 100);
const skip = (currentPage - 1) * currentLimit;
  1. Default values: If page or limit are not provided, defaults are used (page: 1, limit: 20)
  2. Maximum limit: The limit is capped at 100 items to prevent performance issues
  3. Skip calculation: skip = (page - 1) × limit determines how many records to skip
Location: /home/daytona/workspace/source/src/modules/reviews/reviews.service.ts:13

Paginated Endpoints

The following endpoints support pagination:

Reviews

GET /api/routes/:routeId/reviews?page=1&limit=20
Returns paginated reviews for a specific route.

Route Details (Nested Pagination)

GET /api/routes/:slug?reviewsPage=1&reviewsLimit=10&photosLimit=5
The route details endpoint supports nested pagination for related resources:
  • reviewsPage - Page number for reviews (default: 1)
  • reviewsLimit - Limit for reviews (default: 20, max: 100)
  • photosLimit - Limit for photos (default: 10)
Location: /home/daytona/workspace/source/src/modules/routes/routes.controller.ts:53

Best Practices

Start with Reasonable Limits

Use smaller page sizes for better performance:
// Good: Reasonable page size
fetch('/api/routes/ruta-1/reviews?limit=20')

// Avoid: Unnecessarily large page size
fetch('/api/routes/ruta-1/reviews?limit=100')

Check hasNextPage for Infinite Scroll

let currentPage = 1;
const loadMore = async () => {
  const response = await fetch(`/api/routes/ruta-1/reviews?page=${currentPage}&limit=20`);
  const data = await response.json();
  
  // Add items to your list
  items.push(...data.data);
  
  // Check if there are more pages
  if (data.pagination.hasNextPage) {
    currentPage++;
    // Enable "Load More" button
  } else {
    // Hide "Load More" button
  }
};

Calculate Total Pages

Use totalPages to build page navigation:
const { totalPages, page } = data.pagination;

// Render page numbers
for (let i = 1; i <= totalPages; i++) {
  renderPageButton(i, i === page);
}

Ordering

Paginated results are ordered by createdAt in descending order (newest first) by default:
// From reviews.service.ts:47
orderBy: { createdAt: "desc" }
This ensures the most recent items appear first in the list.

Build docs developers (and LLMs) love