FastAPI — Types Are the Contract
Design principles: Cursor pagination without gaps or duplicates
Summary
The contract of pagination is not "how many to show" but where you have read up to, in which order.
Why this matters
Suppose you look at page 1 of a list and then a new item is inserted at the front. If you request the next page with offset=10, you may see an item that was pushed back from the front again. Conversely, a deletion can make you skip an item. Sorting by a unique, unchanging id and reading the rows greater than the last id reduces this shifting problem. But a cursor is not a snapshot of the whole data. It does not guarantee anything about rows inserted later into a range you have already passed, and for lists whose sort value changes, such as sorting by price, you need a composite key and a separate contract.
How it works
전체 행 → 범주 필터 → id 오름차순 → id > cursor → limit + 남은 행 확인
↓
공개 필드 투영 + next_cursor
The fact that there are two results when limit=2 does not by itself tell you there is a next page. In this lab, the cursor is built from the last returned id only when more rows remain than limit. In production SQL, you would read one more row with WHERE id > ? ORDER BY id LIMIT (limit+1) to make the same decision. Here we first verify the contract on a small in-memory list.
What it looks like in the field
Turning a cursor into base64 does not make it encryption or access control. After decoding, check that it is a positive integer, and in a real service decide whether to bind the cursor to the filter and sort conditions. The cursor in this lab holds only the id, so the next request must also send the same category. If you return internal fields such as cost as the original dictionary, data leaks even when pagination is correct. Put a projection at the end that puts only the public fields into a new dictionary.
What you will do in the next lab
You check irregular id order, an empty list, exactly the last page, and an invalid limit and cursor. In the last step you read two pages in a row with TestClient and check for duplicates, omissions, and exposure of internal fields. The lab is finished only when you can explain what would change if the category of the next request differs or the sort key is modified.
See also: FastAPI testing