
Security News
GPT-6 Astra Attempts Supply Chain Attacks Against Open Source Maintainers in Testing
GPT-6 Astra hits 100% on ExploitBench and finds zero-days autonomously, while independent tests reveal scope violations and monitoring gaps.
fluent-wp-client
Advanced tools
Runtime-agnostic TypeScript WordPress REST API client with typed CRUD, auth, abilities, relation helpers, and an optional Gutenberg block add-on
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).
npm install fluent-wp-client
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' });
.describe(), .explore(), or CLI-generated artifacts in application code..describe() and wp.explore(), making the client adaptive to custom post types, taxonomies, plugin endpoints, and ACF fields.supports removing fields like title, content, or author.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.structuredClone(), JSON.stringify(), and cross-boundary transport (SSR, RSC, postMessage).content('posts'), content('pages'), content('books'), and terms('genre') share one API shape, with stricter typing for built-in resourcesmedia(), comments(), users(), and settings() expose consistent list/item/create/update/delete/describe APIs, with upload() for media and me() for userssearchContent() queries across posts, pages, and CPTs via the /wp/v2/search endpointlistAll() fetches all pages in parallel batches for dramatic speed improvements on large datasetsonRequest callback lets you implement custom rate limiting or request logging_embed by default; opt in with embed: true or selective embed: ['author', 'wp:term'] and use typed extraction helperstitle, content, excerpt, or authorfluent-wp-client/blocks adds block type discovery, generated block JSON Schemas, parse/serialize/validate helpers, and explicit .blocks().get() / .blocks().set() content workflowsfluent-wp-client/ai-sdk exposes manually composable Vercel AI SDK toolsfluent-wp-client ships a CLI for discovering resource schemas and generating TypeScript, JSON Schema, and Zod outputs.describe(), .explore(), and CLI schema generation for app-level Zod, Valibot, or custom validationgetEmbeddedAuthor(), getEmbeddedTerms(), getEmbeddedFeaturedMedia(), getEmbeddedParent(), plus ACF helpers like getAcfFieldPosts() and getAcfFieldTerms()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.
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.
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.
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.ts
The 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.
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();
},
});
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.
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.
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 });
// 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' },
});
Full documentation lives in the docs/ folder:
fluent-wp-client/blocks add-on for block type discovery, parsing, validation, and writes.describe(), .explore(), and runtime Zod conversion# Start the local WordPress environment
npm run wp:start
# Run the integration test suite
npm test
# Stop the environment
npm run wp:stop
Core 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.
GPL-2.0-or-later
FAQs
Runtime-agnostic TypeScript WordPress REST API client with typed CRUD, auth, abilities, relation helpers, and an optional Gutenberg block add-on
The npm package fluent-wp-client receives a total of 45 weekly downloads. As such, fluent-wp-client popularity was classified as not popular.
We found that fluent-wp-client demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
GPT-6 Astra hits 100% on ExploitBench and finds zero-days autonomously, while independent tests reveal scope violations and monitoring gaps.

Product
Socket can now send alerts and supply chain attack notifications to Microsoft Teams, with filters that route the right updates to each channel.

Security News
pnpm 12 rewrites the package manager in Rust, cutting install times by up to 90% while preserving pnpm 11 workflows and lockfiles.