All open source
LibraryActiveTypeScript

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-fetch

Query

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

What is under it

TypeScriptWordPress REST APINext.jsESM

Work with us

Have something you want built?

Small enough to care about the details. Experienced enough to build the difficult parts. Tell us about the problem and we'll come back with a technical direction, scope, and timeline.

You work directly with the people building your product.