Never return everything
GET /posts on a real app could mean two million rows. Sending them all would crush the database, the network, and the client. So list endpoints paginate: they return one page at a time.
The common style is page-based: GET /posts?page=2&perPage=20. The math to slice a page out of a list:
start = (page − 1) × perPage
end = start + perPagePage 1 of 20 covers items 0 to 19, page 2 covers 20 to 39, and so on. Run it below on a small array.
Code exercise · javascript
Run this. Seven items, three per page: pages 1 and 2 are full, page 3 holds the remainder.
Tell the client where it stands
A bare array is a rude answer, because the client cannot render "Page 2 of 34" or know whether to show a Next button. Good list endpoints wrap the data with meta:
{
"data": ["d", "e", "f"],
"meta": { "page": 2, "perPage": 3, "total": 7, "totalPages": 3 }
}totalPages is Math.ceil(total / perPage), rounding up so a leftover half-page still counts.
Versioning
Once someone depends on your API, changing a response shape breaks their app. The standard escape hatch is a version prefix: /v1/posts today, and when you must change shapes, /v2/posts alongside it while v1 keeps working. Version from day one, retrofitting is painful.
Quiz
Your API returns user objects with a "name" field. You want to split it into "firstName" and "lastName". Mobile apps in production read "name" today. What is the safe move?
Code exercise · javascript
Your turn. Implement paginate(items, page, perPage) returning { data, meta } where meta has page, perPage, total (item count), and totalPages (use Math.ceil).