Asking a librarian for 'everything about history' and being handed the entire card catalog at once isn't useful — you'd drop half of it before finding what you needed. Asking for one drawer at a time, with a note on where to look next, gets you the same information in a form you can actually carry. Pagination applies that same idea to an API response: a bounded page of results, plus a cursor pointing at the next one, instead of the whole table at once.
The performance work I did on Magento projects at Infracommerce often started with the same root cause pagination guards against: analyzing a project to find its slow queries and bottlenecks, and finding an endpoint that assumed a small result set would always stay small — until a client's catalog or order history grew past that assumption and every request touching it got expensive. Bounding a response to a page and a cursor, instead of asking a query to hand back everything at once, is the API-shaped version of that same fix.
Trade-off: a cursor-paginated endpoint can't jump straight to page 40 the way an offset can — client code that used to fetch everything in one call now has to loop through pages, and 'give me the whole export' becomes a real use case someone has to design for separately.
Before
class ProductController
{
public function index(): JsonResponse
{
// every product, every request —
// fine at 50 rows, not at 500,000
return response()->json(Product::all());
}
}After
class ProductController
{
public function index(): JsonResponse
{
$page = Product::query()
->orderBy('id')
->cursorPaginate(50);
return response()->json($page);
}
}