fluent-wp-client
v3.0.1
Published
Runtime-agnostic TypeScript WordPress REST API client with typed CRUD, auth, abilities, relation helpers, and an optional Gutenberg block add-on
Maintainers
Readme
fluent-wp-client
Runtime-agnostic TypeScript client for the WordPress REST API.
Works on Node.js, Deno, Bun, and in the browser — using only web-standard APIs (fetch, URL, Blob).
Install
npm install fluent-wp-clientQuick start
import { WordPressClient } from 'fluent-wp-client';
const wp = new WordPressClient({
baseUrl: 'https://your-wordpress-site.com',
});
const posts = wp.content('posts');
// Read a list of posts
const recentPosts = await posts.list({ perPage: 10 });
// First-class resources use the same fluent style
const comments = await wp.comments().list({ post: 42 });
// Read a single post
const post = await posts.item('hello-world');
// Create a draft post (requires auth)
const draft = await posts.create({ title: 'Hello', status: 'draft' });Design principles
- WordPress is the source of truth for validation. Request and mutation helpers defer validation to the REST API. When you want local validation, use
.describe(),.explore(), or CLI-generated artifacts in application code. - Schemas are discoverable at runtime. Every resource and ability exposes its live JSON Schema through
.describe()andwp.explore(), making the client adaptive to custom post types, taxonomies, plugin endpoints, and ACF fields. - Strict defaults for built-ins, flexible defaults for everything else. Native posts and pages get strict typing; generic CPTs default to a post-like shape that tolerates WordPress
supportsremoving fields liketitle,content, orauthor. - Extension by composition, not hard-coding.
content(resource),terms(resource), ACF relation extraction, and ability builders stay generic so projects can layer in custom fields, meta, relations, and plugin data without fighting the client. - Runtime-agnostic and serialization-safe. Terminal reads return plain DTOs that survive
structuredClone(),JSON.stringify(), and cross-boundary transport (SSR, RSC,postMessage).
Features
- Unified typed content builders —
content('posts'),content('pages'),content('books'), andterms('genre')share one API shape, with stricter typing for built-in resources - First-class fluent resource clients —
media(),comments(),users(), andsettings()expose consistentlist/item/create/update/delete/describeAPIs, withupload()for media andme()for users - Cross-resource search —
searchContent()queries across posts, pages, and CPTs via the/wp/v2/searchendpoint - Parallel bulk fetching —
listAll()fetches all pages in parallel batches for dramatic speed improvements on large datasets - Rate limiting support —
onRequestcallback lets you implement custom rate limiting or request logging - Extensible collection filters — built-in list helpers and generic resource builders accept typed core filters plus extra endpoint-specific query params
- Lean embedded payloads — post-like DTO reads skip
_embedby default; opt in withembed: trueor selectiveembed: ['author', 'wp:term']and use typed extraction helpers - Flexible CPT defaults — generic content reads tolerate post types that omit
title,content,excerpt, orauthor - Portable Gutenberg block add-on —
fluent-wp-client/blocksadds block type discovery, generated block JSON Schemas, parse/serialize/validate helpers, and explicit.blocks().get()/.blocks().set()content workflows - Framework-neutral agent helpers — root package APIs expose JSON Schema selectors, fields, and query argument helpers for Flue, MCP, LangChain, and custom agents
- AI SDK tool factories —
fluent-wp-client/ai-sdkexposes manually composable Vercel AI SDK tools - CLI schema/code generation —
fluent-wp-clientships a CLI for discovering resource schemas and generating TypeScript, JSON Schema, and Zod outputs - Auth flexibility — Basic auth (application passwords), JWT, cookie+nonce, prebuilt headers, and per-request signing
- WordPress Abilities API — discover and execute registered abilities with the same upstream-validated model as the rest of WordPress
- Schema discovery and custom validation — validator-agnostic root exports plus
.describe(),.explore(), and CLI schema generation for app-level Zod, Valibot, or custom validation - Embed extraction helpers —
getEmbeddedAuthor(),getEmbeddedTerms(),getEmbeddedFeaturedMedia(),getEmbeddedParent(), plus ACF helpers likegetAcfFieldPosts()andgetAcfFieldTerms()
Gutenberg block workflows
Use the dedicated block add-on when you need block parsing, validation, or block type discovery:
import { WordPressClient } from 'fluent-wp-client';
import { withBlocks } from 'fluent-wp-client/blocks';
const wp = new WordPressClient({
baseUrl: 'https://example.com',
auth: { username: 'admin', password: 'app-password' },
});
const wpBlocks = withBlocks(wp);
const blockSchemas = await wpBlocks.blocks().schemas();
const postQuery = wpBlocks.content('posts').item('hello-world');
const blocks = await postQuery.blocks().get({
schemas: blockSchemas,
validate: true,
});
blocks[0].innerHTML = '<p>Updated paragraph body.</p>';
blocks[0].innerContent = ['<p>Updated paragraph body.</p>'];
await postQuery.blocks().set(blocks, blockSchemas);Standalone helpers are also available when you already have raw content.raw:
import {
createWordPressBlockJsonSchemas,
parseWordPressBlocks,
serializeWordPressBlocks,
validateWordPressBlocks,
} from 'fluent-wp-client/blocks';
const blockTypes = await wpBlocks.blocks().list();
const blockSchemas = createWordPressBlockJsonSchemas(blockTypes);
const blocks = await parseWordPressBlocks(content.raw);
const result = await validateWordPressBlocks(blocks, {
schemas: blockSchemas,
});
if (result.valid) {
const nextContent = await serializeWordPressBlocks(blocks);
}blocks().set() does not implicitly fetch block types. Pass the schemas you want to allow when you need whitelist validation.
If you want native Zod schemas for AI-produced block trees, import them from fluent-wp-client/blocks/zod.
AI SDK Integration
Use the optional fluent-wp-client/ai-sdk entrypoint when you want manually composable Vercel AI SDK tools built on top of your WordPress client instance.
import { WordPressClient } from 'fluent-wp-client';
import {
getContentCollectionTool,
getContentTool,
saveContentTool,
} from 'fluent-wp-client/ai-sdk';
const wp = new WordPressClient({
baseUrl: 'https://example.com',
auth: { username: 'editor', password: 'app-password' },
});
await wp.explore();
const tools = {
searchPosts: getContentCollectionTool(wp, {
contentType: 'posts',
fixedArgs: { perPage: 5, status: 'publish' },
}),
readContent: getContentTool(wp),
writePost: saveContentTool(wp, {
contentType: 'posts',
fixedInput: { status: 'draft' },
}),
};saveContentTool handles both create and update — call it without id to create, or with id to update. One tool surface, one mental model.
Every tool factory accepts an optional fetch callback that replaces the default WordPress client call with a custom implementation — useful for caches, live loaders, proxies, or custom request pipelines. Works for reads and writes alike.
Use toolOptions to pass additional AI SDK tool settings such as title, inputExamples, lifecycle callbacks, outputSchema, or toModelOutput without replacing the generated WordPress execution and schema behavior.
See docs/ai-sdk.mdx for the full tool catalog and configuration model.
Framework-Neutral Agent Tools
Use root package helpers when your agent framework needs JSON Schema, selectors, fields, or query arguments to register its own tools.
await wp.explore();
const selectors = await wp.getCatalogSelectors();
const inputSchema = await wp.content('posts').getJsonSchema('create');
const queryParams = await wp.content('posts').getQueryParams();Define framework-specific tool names, descriptions, defaults, fixed arguments, and mutation guardrails in your adapter. The package provides the catalog-derived building blocks.
CLI
Use the built-in CLI to discover WordPress REST schemas and generate code artifacts:
npx fluent-wp-client schemas --url https://example.com
npx fluent-wp-client schemas --url https://example.com --types-out wp-types.d.ts
npx fluent-wp-client schemas --url https://example.com --zod-out wp-schemas.mjs --types-out wp-types.d.ts
npx fluent-wp-client schemas --url https://example.com --zod-out wp-schemas.ts --json-out wp-schemas.json --types-out wp-types.d.tsThe schemas command discovers WordPress JSON Schema, then emits the artifacts requested by output paths. Omit output paths to write wp-schemas.ts, or pass paths such as --zod-out wp-schemas.mjs, --json-out wp-schemas.json, and --types-out wp-types.d.ts. Zod output is intentionally limited to .ts or .mjs; JSON Schema uses .json; standalone types use .d.ts.
Generated schemas relax WordPress-specific response quirks centrally: optional scalar ACF fields can validate as "", and registered meta fields accept WordPress' declared empty defaults. Authenticated runtime catalogs include catalog.site.timezone, keep strict date-time schemas for WordPress date fields, and automatically normalize subsequent DTO date values to offset ISO datetime strings. Without timezone metadata, schemas and DTOs fall back to WordPress' raw local datetime strings.
Rate limiting and request control
Use the onRequest callback to implement rate limiting, request logging, or other custom logic:
// Simple delay between requests
const wp = new WordPressClient({
baseUrl: 'https://example.com',
onRequest: async (url, init) => {
await new Promise(resolve => setTimeout(resolve, 100));
},
});// Token bucket rate limiter
const rateLimiter = new TokenBucketLimiter({ tokensPerSecond: 10, maxTokens: 20 });
const wpWithRateLimit = new WordPressClient({
baseUrl: 'https://example.com',
onRequest: async (url, init) => {
await rateLimiter.acquireToken();
},
});Parallel bulk fetching
listAll() automatically fetches pages in parallel for maximum performance. It fetches the first page to get the total count, then spawns parallel requests for remaining pages in configurable batches:
// Fetch all posts with default concurrency (5 pages at a time)
const allPosts = await wp.content('posts').listAll();
// Higher concurrency for faster fetching (use with caution)
const allUsers = await wp.users().listAll({}, {}, { concurrency: 10 });
// Lower concurrency to be gentler on the server
const allComments = await wp.comments().listAll({}, {}, { concurrency: 2 });The explore() API also makes parallel requests when discovering all resources, dramatically speeding up schema discovery.
Flexible collection filters
Collection helpers keep typed core filters like search, include, exclude, and slug, and they also forward extra endpoint-specific params for plugin or project-specific REST extensions.
const posts = await wp.content('posts').list({
search: 'hello world',
include: [4, 8],
titleSearch: 'Hello',
});
const books = await wp.content('books').list({
include: [164, 165],
titleSearch: 'Test Book',
});Array params are serialized as repeated param[] entries instead of comma-joined strings.
Embedded data
Post-like DTO reads stay lean by default. Pass embed: true or a selective embed array to include related data, then use typed extraction helpers to pull it out:
import { getEmbeddedAuthor, getEmbeddedTerms } from 'fluent-wp-client';
const post = await wp.content('posts').item('hello-world', { embed: true });
const author = getEmbeddedAuthor(post);
const categories = getEmbeddedTerms(post, 'category');
// Selective embed reduces server-side work
const post2 = await wp.content('posts').item('hello-world', {
embed: ['author', 'wp:term'],
});
// Embed on lists
const posts = await wp.content('posts').list({ perPage: 10, embed: true });Auth examples
// Basic auth (WordPress application passwords)
const wp = new WordPressClient({
baseUrl: 'https://example.com',
auth: { username: 'admin', password: 'app-password' },
});
// JWT auth
const wp = new WordPressClient({
baseUrl: 'https://example.com',
auth: { token: 'jwt-token' },
});
// Cookie + nonce (browser sessions)
const wp = new WordPressClient({
baseUrl: 'https://example.com',
auth: { nonce: window.wpApiSettings.nonce, credentials: 'include' },
});Documentation
Full documentation lives in the docs/ folder:
- Overview — feature overview and quick start
- Usage guide — all client methods, CRUD patterns, pagination, and error handling
- Authentication — auth strategies and resolver helpers
- Gutenberg content — the
fluent-wp-client/blocksadd-on for block type discovery, parsing, validation, and writes - Custom endpoints — custom post types, taxonomies, and plugin namespaces
- Abilities — WordPress Abilities API
- Validation — Standard Schema, Zod, and custom validators
- Embed extraction — embed parameters, typed extraction helpers, and ACF relation extraction
- Schema discovery —
.describe(),.explore(), and runtime Zod conversion - CLI — generate JSON Schema and Zod artifacts from live WordPress instances
- AI SDK integration — compose WordPress tools for the Vercel AI SDK
Development
# Start the local WordPress environment
npm run wp:start
# Run the integration test suite
npm test
# Stop the environment
npm run wp:stopCore transport, mutation helpers, query primitives, and base resource classes live under src/core/, while query builders live under src/builders/. Package consumers continue to import the public API from src/index.ts.
Tests run against a real WordPress Docker container managed by @wordpress/env. See tests/ for setup details.
License
GPL-2.0-or-later
