unpaginated

Dead simple pagination. unpaginated executes a paginated function until done, gathering results as efficiently as possible.

Usage no npm install needed!

<script type="module">
  import unpaginated from 'https://cdn.skypack.dev/unpaginated';
</script>

README

unpaginated

Dead simple pagination. unpaginated executes a paginated function until done, gathering results as efficiently as possible.

import fetch from 'node-fetch';
import unpaginated from 'unpaginated';

// Say our API has 100 posts available
const url = 'http://api.example';

const fetchPosts = page =>
  fetch(`${url}/posts?_page=${page}&_limit=20`)
    .then(res => res.json())
    .then(({ posts }) => posts);

unpaginated(fetchPosts);

Many APIs include a total property in the payload. Just include it and unpaginated will run concurrently:

import fetch from 'node-fetch';
import unpaginated from 'unpaginated';

const url = 'http://api.example';

const fetchPostsWithTotal = page =>
  fetch(`${url}/posts?page=${page}&limit=20`)
    .then(res => res.json())
    .then(({ posts, total }) => ({ data: posts, total }));

unpaginated(fetchPostsWithTotal);

For APIs that don't talk pages, use byOffset:

import fetch from 'node-fetch';
import { byOffset } from 'unpaginated';

const url = 'http://api.example';

const fetchPosts = offset =>
  fetch(`${url}/posts?offset=${offset}&limit=20`)
    .then(res => res.json())
    .then(({ posts }) => posts);

byOffset(fetchPosts);

unpaginated also understands cursor-based pagination with byCursor:

import fetch from 'node-fetch';
import { byCursor } from 'unpaginated';

const url = 'http://api.example';

// cursor is undefined the first call, so we'll set a default
const fetchPostsWithCursor = (cursor = '') =>
  fetch(`${url}/posts?cursor=${cursor}&limit=20`)
    .then(res => res.json())
    .then(({ posts, cursor }) => ({ data: posts, cursor }));

byCursor(fetchPostsWithCursor);

API

unpaginated

Alias for byPage.

byPage

// byPage :: (Int -> Promise ([a] | { data: [a], total: Int })) -> Promise [a]

// serial
byPage(page => fetch(`/users?page=${page}`)
  .then(res => res.json())
);

// concurrent
byPage(page => fetch(`/users?page=${page}`)
  .then(res => res.json()))
  .then(({ data, total }) => ({ data, total })
);

byOffset

// byOffset :: (Int -> Promise ([a] | { data: [a], total: Int })) -> Promise [a]

// serial
byOffset(offset => fetch(`/users?offset=${offset}`)
  .then(res => res.json())
);

// concurrent
byOffset(offset => fetch(`/users?offset=${offset}`)
  .then(res => res.json()))
  .then(({ data, total }) => ({ data, total })
);

byCursor

// byCursor :: ((String | Int) -> Promise { data: [a], cursor: (String | Int) }) -> Promise [a]

byCursor(cursor => fetch(`/users?cursor=${cursor || ''}`)
  .then(res => res.json())
);

CommonJS

const { default: unpaginated, byPage, byCursor, byOffset } = require('unpaginated');