wp-fetch
A TypeScript client for the WordPress REST API
Framework-agnostic, with zero runtime dependencies in the core and an optional Next.js cache adapter. Handles the parts of the WordPress REST API that everyone re-implements badly: pagination, relations, and cache keys.
Language
TypeScript
Licence
MIT
Status
Active
Repository
webloomlabs/wp-fetch
Requires
Node 22.12+, ESM only
Peer
Next.js 16+, optional
Why it exists
Everyone writes
this wrapper once, badly
Every headless WordPress project starts with the same fifty lines: a fetch wrapper, a pagination loop, and a guess at how to get the author's name onto the post. Those fifty lines are where the bugs live.
WordPress returns HTTP 400 rather than an empty array past the final page, so the naive loop throws on the last request. `_embed` returns partial author and media objects, so the fields you wanted are missing exactly when the data matters. The `total` header is absent on some routes, so a type that promises a number is lying.
wp-fetch handles those cases once, in a package with no runtime dependencies — the core does not even import `next`. The Next.js cache adapter is opt-in, so the same client works in a script, a worker, or another framework entirely.
What it does
What wp-fetch gives you
The short version of the README. The repository has the rest, including what wp-fetch does not do yet.
Zero runtime dependencies
The core imports nothing. Next.js is an optional peer, so the client runs anywhere fetch does.
Pagination that terminates
Including the HTTP 400 WordPress returns past the final page, which is what breaks the loop everyone writes by hand.
Keyset iteration
`stream()` walks large datasets without the offset drift that makes deep pagination skip and repeat rows.
Full-fidelity relations
Batch hydration returns complete author and media objects, rather than the partial ones `_embed` gives you.
Deterministic cache tags
`tagsFor()` produces stable tags, so a webhook can expire exactly the pages a change affected instead of the whole site.
Honest types
`total` is nullable because WordPress genuinely omits it on some routes. The types describe the API as it behaves, not as it is documented.
Getting started
Install and query
Install
npm install wp-fetchQuery
import { createWPClient } from "wp-fetch";
const wp = createWPClient({ url: "https://cms.example.com" });
const posts = await wp.posts.list({ perPage: 12 });
const post = await wp.posts.bySlug("hello-world");
const categories = await wp.categories.list({ hideEmpty: true });Every resource exposes list, get, find, bySlug, byIds, count, all, and stream, plus tagsFor and urlFor.
Built with