Skip to content

Commit 55c5cc9

Browse files
committed
feat: add pagination support for item listings and enhance data fetching
1 parent 17287e0 commit 55c5cc9

2 files changed

Lines changed: 93 additions & 34 deletions

File tree

README.md

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,26 @@ These endpoints let you list everything or get a specific item:
3838

3939
#### Items
4040
```bash
41-
GET /v1/items # List all item IDs (490 items)
42-
GET /v1/items?full=true # Get ALL items with full data (1 request!)
43-
GET /v1/items/{item_id} # Get a specific item
41+
GET /v1/items # List all item IDs (490 items)
42+
GET /v1/items?full=true # Get first 45 items with full data
43+
GET /v1/items?full=true&limit=20 # Get first 20 items with full data
44+
GET /v1/items?full=true&offset=45 # Get next 45 items (pagination)
45+
GET /v1/items/{item_id} # Get a specific item
4446
```
4547

4648
**Examples:**
4749
```bash
4850
# Get just the list of IDs
4951
curl https://arcdata.mahcks.com/v1/items
5052

51-
# Get all items with complete data in one request
53+
# Get first 45 items with complete data (respects Cloudflare limits)
5254
curl https://arcdata.mahcks.com/v1/items?full=true
5355

56+
# Paginate through all items
57+
curl https://arcdata.mahcks.com/v1/items?full=true&offset=0&limit=45
58+
curl https://arcdata.mahcks.com/v1/items?full=true&offset=45&limit=45
59+
curl https://arcdata.mahcks.com/v1/items?full=true&offset=90&limit=45
60+
5461
# Get a specific item
5562
curl https://arcdata.mahcks.com/v1/items/anvil_i
5663
```
@@ -120,7 +127,7 @@ Returns the complete JSON data for whatever you requested.
120127

121128
## Performance Tips
122129

123-
### Use `?full=true` to avoid multiple requests
130+
### Use `?full=true` with pagination to avoid multiple requests
124131

125132
Instead of making hundreds of requests:
126133
```javascript
@@ -131,15 +138,46 @@ const items = await Promise.all(
131138
);
132139
```
133140

134-
Use the `?full=true` parameter:
141+
Use the `?full=true` parameter with pagination:
135142
```javascript
136-
// ✅ GOOD - Makes 1 request total
137-
const { items } = await fetch('/v1/items?full=true').then(r => r.json());
143+
// ✅ GOOD - Fetch all items in pages (11 requests for 490 items)
144+
async function getAllItems() {
145+
let allItems = [];
146+
let offset = 0;
147+
const limit = 45;
148+
149+
while (true) {
150+
const response = await fetch(`/v1/items?full=true&offset=${offset}&limit=${limit}`);
151+
const data = await response.json();
152+
153+
allItems.push(...data.items);
154+
155+
// Check if there's more data
156+
if (!data.next) break;
157+
offset += limit;
158+
}
159+
160+
return allItems;
161+
}
138162
```
139163

140164
**Performance difference:**
141165
- Without `?full=true`: **491 requests** (~5-10 seconds)
142-
- With `?full=true`: **1 request** (~500ms)
166+
- With `?full=true` + pagination: **11 requests** (~2-3 seconds)
167+
- Each paginated request is cached separately!
168+
169+
**Response includes pagination URLs:**
170+
```json
171+
{
172+
"type": "items",
173+
"total": 490,
174+
"count": 45,
175+
"offset": 0,
176+
"limit": 45,
177+
"items": [...],
178+
"next": "/v1/items?full=true&offset=45&limit=45"
179+
}
180+
```
143181

144182
## How Caching Works
145183

src/index.js

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ export default {
5252
if (COLLECTION_TYPES[type]) {
5353
// Check if user wants full data via ?full=true
5454
const full = url.searchParams.get('full') === 'true';
55-
return await handleList(type, env, ctx, full);
55+
const limit = parseInt(url.searchParams.get('limit')) || undefined;
56+
const offset = parseInt(url.searchParams.get('offset')) || 0;
57+
return await handleList(type, env, ctx, full, limit, offset);
5658
}
5759

5860
return jsonResponse({ error: `Unknown data type: ${type}` }, 404);
@@ -191,8 +193,10 @@ async function handleGetItem(type, id, env, ctx) {
191193
* List all items of a type
192194
* Uses GitHub API to dynamically list directory contents
193195
* @param {boolean} full - If true, fetch and return full data for all items
196+
* @param {number} limit - Max items to return (default: 45 for full, all for list)
197+
* @param {number} offset - Skip this many items (for pagination)
194198
*/
195-
async function handleList(type, env, ctx, full = false) {
199+
async function handleList(type, env, ctx, full = false, limit = undefined, offset = 0) {
196200
if (!COLLECTION_TYPES[type]) {
197201
return jsonResponse({ error: `Unknown collection type: ${type}` }, 404);
198202
}
@@ -222,43 +226,60 @@ async function handleList(type, env, ctx, full = false) {
222226
const files = await githubResponse.json();
223227

224228
// Filter to only .json files
225-
const jsonFiles = files
229+
const allJsonFiles = files
226230
.filter((f) => f.type === 'file' && f.name.endsWith('.json') && !f.name.startsWith('_'))
227231
.map((f) => f.name.replace('.json', ''))
228232
.sort((a, b) => a.localeCompare(b));
229233

234+
const totalItems = allJsonFiles.length;
235+
236+
// Apply pagination
237+
let jsonFiles = allJsonFiles;
238+
if (offset > 0) {
239+
jsonFiles = jsonFiles.slice(offset);
240+
}
241+
230242
let data;
231243

232244
if (full) {
233-
// Cloudflare Workers has a 50 subrequest limit, so batch the fetches
234-
const BATCH_SIZE = 50;
235-
const allItems = [];
236-
237-
for (let i = 0; i < jsonFiles.length; i += BATCH_SIZE) {
238-
const batch = jsonFiles.slice(i, i + BATCH_SIZE);
239-
const batchPromises = batch.map(async (id) => {
240-
const itemUrl = `${GITHUB_BASE}/${dirPath}/${id}.json`;
241-
const itemResponse = await fetch(itemUrl, {
242-
headers: { 'User-Agent': 'ArcRaiders-API/1.0' },
243-
});
244-
245-
if (itemResponse.ok) {
246-
return await itemResponse.json();
247-
}
248-
return null;
245+
// Cloudflare Workers free tier has 50 subrequest limit
246+
// We already used 1 for the GitHub API call, so we have 49 left
247+
const MAX_ITEMS = 45; // Leave some buffer
248+
const effectiveLimit = limit && limit < MAX_ITEMS ? limit : MAX_ITEMS;
249+
const limitedFiles = jsonFiles.slice(0, effectiveLimit);
250+
251+
const itemPromises = limitedFiles.map(async (id) => {
252+
const itemUrl = `${GITHUB_BASE}/${dirPath}/${id}.json`;
253+
const itemResponse = await fetch(itemUrl, {
254+
headers: { 'User-Agent': 'ArcRaiders-API/1.0' },
249255
});
250256

251-
const batchResults = await Promise.all(batchPromises);
252-
allItems.push(...batchResults.filter(Boolean));
253-
}
257+
if (itemResponse.ok) {
258+
return await itemResponse.json();
259+
}
260+
return null;
261+
});
262+
263+
const itemsData = await Promise.all(itemPromises);
254264

255265
data = {
256266
type,
257-
count: allItems.length,
258-
items: allItems,
267+
total: totalItems,
268+
count: itemsData.filter(Boolean).length,
269+
offset,
270+
limit: effectiveLimit,
271+
items: itemsData.filter(Boolean),
259272
};
273+
274+
// Add pagination hints
275+
if (offset + effectiveLimit < totalItems) {
276+
data.next = `/v1/${type}?full=true&offset=${offset + effectiveLimit}&limit=${effectiveLimit}`;
277+
}
278+
if (offset > 0) {
279+
data.prev = `/v1/${type}?full=true&offset=${Math.max(0, offset - effectiveLimit)}&limit=${effectiveLimit}`;
280+
}
260281
} else {
261-
// Just return IDs and URLs (original behavior)
282+
// Just return IDs and URLs (original behavior, no limit)
262283
const items = jsonFiles.map((id) => ({
263284
id,
264285
url: `/v1/${type}/${id}`,

0 commit comments

Comments
 (0)