Usage – fluent-wp-client
API styles
Section titled “API styles”const posts = wp.content('posts');const recentPosts = await posts.list({ perPage: 10 });const post = await posts.item('hello-world');
const books = wp.content('books');const allBooks = await books.listAll();
const genres = await wp.terms('genre').list({ perPage: 100 });const media = await wp.media().list({ perPage: 20 });const me = await wp.users().me();const comments = await wp.comments().list({ post: 42 });const settings = await wp.settings().get();const { data, response } = await wp.request({ endpoint: '/wp-json/my-plugin/v1/sync', method: 'POST', body: { mode: 'full' },});const siteTitle = await wp.executeGetAbility<{ title: string }>('test/get-site-title');
const result = await wp .ability<{ name: string }, { processed: boolean }>('test/process-complex') .run({ name: 'demo' });Method reference
Section titled “Method reference”Content and term builders
Section titled “Content and term builders”content('posts') / content('pages') / content(resource)terms('categories') / terms('tags') / terms(resource)Each builder exposes:
.list(filter?)— one page.listAll(filter?)— auto-paginates all pages.listPaginated(filter?)— returns{ data, total, totalPages, page, perPage }.item(idOrSlug)— single item (awaitable).create()/.update()/.delete()— mutations.describe()— JSON Schema discovery
content(resource).item(idOrSlug) also supports .getContent() for { raw, rendered, protected }. Pass { embed: true } or { embed: ['author', 'wp:term'] } to request embedded relations.
First-class resources
Section titled “First-class resources”| Resource | Extras |
|---|---|
media() | .upload(), .getImageUrl() |
users() | .me() |
comments() | — |
settings() | .get(), .update(), .describe() (singleton) |
All collection resources share .list(), .listAll(), .listPaginated(), .item(), .create(), .update(), .delete(), .describe(). Pass { embed: true } on .item() or .list() to request embedded data.
Search
Section titled “Search”const results = await wp.searchContent('hello');const pages = await wp.searchContent('about', { type: 'post', subtype: 'page' });const mixed = await wp.searchContent('test', { subtype: ['post', 'page', 'book'] });Returns { id, title, url, type, subtype }[].
Extensible collection filters
Section titled “Extensible collection filters”Typed core filters accept extra unknown keys. CamelCase names become snake_case query params automatically:
const posts = await wp.content('posts').list({ search: 'hello', include: [4, 8], titleSearch: 'Hello', // becomes title_search});Request-scoped headers
Section titled “Request-scoped headers”All read and mutation helpers accept optional custom headers:
const posts = await wp.content('posts').list( { perPage: 5 }, { headers: { 'x-custom-header': 'value' } },);Auth and ability helpers
Section titled “Auth and ability helpers”loginWithJwt(credentials)/validateJwtToken(token?)getAbilities()/getAbility(name)/getAbilityCategories()executeGetAbility()/executeRunAbility()/executeDeleteAbility()ability<TInput, TOutput>(name)— fluent builder with.get(),.run(),.delete(),.describe()
Transport
Section titled “Transport”request(options)— low-level REST request with response metadatasetHeaders(name, value)/hasAuth()getRuntime()— internal runtime access for add-ons and wrappers
Embed and extraction helpers
Section titled “Embed and extraction helpers”WordPress’s native _embed parameter tells the server to inline related data (author, terms, featured media, etc.) directly in the response. Extraction helpers let you pull typed data out of the embedded response without manual casting.
Requesting embeds
Section titled “Requesting embeds”Pass embed as an option on .item() or .list():
const post = await wp.content('posts').item('hello-world', { embed: true });const posts = await wp.content('posts').list({ perPage: 10, embed: true });const post = await wp.content('posts').item('hello-world', { embed: ['author', 'wp:term'],});Extracting typed data
Section titled “Extracting typed data”Import extraction helpers from the root package:
import { getEmbeddedAuthor, getEmbeddedTerms, getEmbeddedFeaturedMedia, getEmbeddedParent,} from 'fluent-wp-client';
const post = await wp.content('posts').item('hello-world', { embed: true });
const author = getEmbeddedAuthor(post); // WordPressAuthor | nullconst categories = getEmbeddedTerms(post, 'category'); // WordPressCategory[]const tags = getEmbeddedTerms(post, 'post_tag'); // WordPressTag[]const media = getEmbeddedFeaturedMedia(post); // WordPressMedia | nullconst parent = getEmbeddedParent(post); // WordPressPostLike | nullDiscovering available embed keys
Section titled “Discovering available embed keys”Use getEmbeddableLinkKeys() to inspect which relations a post supports before requesting them:
import { getEmbeddableLinkKeys } from 'fluent-wp-client';
const post = await wp.content('posts').item(1, { fields: ['id', '_links'] });const embeddable = getEmbeddableLinkKeys(post);// ['author', 'wp:term', 'wp:featuredmedia', 'replies', ...]Selective vs full embed
Section titled “Selective vs full embed”| Approach | When to use |
|---|---|
embed: true | You need all relations — author, terms, media, parent, replies |
embed: ['author', 'wp:term'] | You only need specific relations — reduces server work |
| No embed (default) | Lean payloads — fetch relations separately if needed |
ACF relation extraction
Section titled “ACF relation extraction”When ACF fields are configured with show_in_rest: true, ACF relation data can be embedded and extracted:
import { getAcfFieldPosts, getAcfFieldPost, getAcfFieldTerms,} from 'fluent-wp-client';
const post = await wp.content('posts').item('my-post', { embed: ['acf:post', 'acf:term'],});
const relatedPosts = getAcfFieldPosts(post, 'acf_related_posts'); // WordPressPost[]const featuredPost = getAcfFieldPost(post, 'acf_featured_post'); // WordPressPostLike | nullconst relatedTerms = getAcfFieldTerms(post, 'acf_related_genres'); // WordPressTerm[]Embed key reference
Section titled “Embed key reference”| Embed key | Data | Extraction helper |
|---|---|---|
author | Post author | getEmbeddedAuthor() |
wp:term | Categories, tags, custom taxonomies | getEmbeddedTerms() |
wp:featuredmedia | Featured image | getEmbeddedFeaturedMedia() |
up | Parent post/page | getEmbeddedParent() |
replies | Comments | getEmbeddedReplies() |
acf:post | ACF post object / relationship fields | getAcfFieldPosts(), getAcfFieldPost() |
acf:term | ACF taxonomy fields | getAcfFieldTerms() |
Post-like typing
Section titled “Post-like typing”import type { WordPressCustomPost, WordPressPostBase } from 'fluent-wp-client';
type Artifact = WordPressCustomPost<{ type: 'artifact' }>; // sparse — optional fieldstype ContentBook = WordPressPostBase & { type: 'book' }; // strict — requires content fieldsCRUD walkthrough
Section titled “CRUD walkthrough”Posts and pages
Section titled “Posts and pages”const posts = wp.content('posts');const created = await posts.create({ title: 'My post', status: 'draft' });await posts.update(created.id, { title: 'Updated', status: 'publish' });await posts.delete(created.id, { force: true });const cat = await wp.terms('categories').create({ name: 'News' });await wp.terms('categories').update(cat.id, { name: 'Company News' });await wp.terms('categories').delete(cat.id, { force: true });Comments
Section titled “Comments”const comment = await wp.comments().create({ post: 42, content: 'Nice!', status: 'approve' });await wp.comments().update(comment.id, { content: 'Updated.' });await wp.comments().delete(comment.id, { force: true });const media = await wp.media().upload({ file: imageBlob, filename: 'cover.jpg', mimeType: 'image/jpeg', title: 'Cover', alt_text: 'Book cover',});await wp.media().delete(media.id, { force: true });Users and settings
Section titled “Users and settings”const me = await wp.users().me();const settings = await wp.settings().get();await wp.settings().update({ title: 'New Title' });For CPT and custom taxonomy CRUD, see the custom endpoints guide.
Validation
Section titled “Validation”WordPress validates requests upstream. Use .describe(), .explore(), or the CLI when you want local validation in your app:
import { z } from 'zod';
const desc = await wp.content('posts').describe();const createSchema = z.fromJSONSchema(desc.schemas.create!);
const validatedInput = createSchema.parse({ title: 'Validated', status: 'draft' });const created = await wp.content('posts').create(validatedInput);See the validation guide for real-time discovery, explore(), CLI-generated schemas, Astro, and AI SDK patterns.
Gutenberg blocks
Section titled “Gutenberg blocks”import { withBlocks } from 'fluent-wp-client/blocks';
const wpBlocks = withBlocks(wp);const blocks = await wpBlocks.content('posts').item('hello-world').blocks().get();See the Gutenberg content guide for the full workflow.
Pagination
Section titled “Pagination”Error handling
Section titled “Error handling”All runtime failures thrown by the client use WordPressClientError (or a subclass). Use instanceof to narrow to specific error types, or check error.kind for serialization/logging:
WordPressHttpError— non-2xx HTTP responses (includesstatus,statusText,wpCode,wpMessage,responseBody)WordPressAuthError— authentication precondition failuresWordPressNetworkError— fetch-level connectivity failuresWordPressTimeoutError— request timeoutsWordPressParseError— response body parsing failuresWordPressConfigError— missing or invalid client configurationWordPressDiscoveryError— schema discovery failuresWordPressInvalidRequestError— malformed input or precondition violationsWordPressBlockValidationError— block tree validation failures (fromfluent-wp-client/blocks)
Local application-side schema validation stays in your validator of choice after .describe() or .explore(). Those validator-native errors are not wrapped by the client.
import { WordPressClientError, WordPressHttpError } from 'fluent-wp-client';
try { await wp.content('posts').create({ title: 'Hello' });} catch (error) { if (error instanceof WordPressHttpError) { console.log(error.status); // e.g. 401 console.log(error.wpCode); // e.g. 'rest_cannot_create' console.log(error.wpMessage); // e.g. 'Sorry, you are not allowed...' }
// Catch-all for any client error if (error instanceof WordPressClientError) { console.log(error.kind); // e.g. 'WP_API_ERROR' console.log(error.retryable); // Can the caller retry? }}Every error supports toJSON() for structured logging and cross-boundary transport:
catch (error) { if (error instanceof WordPressClientError) { console.log(JSON.stringify(error)); // {"name":"WordPressHttpError","kind":"WP_API_ERROR","message":"...","retryable":false,"status":401,...} }}Cross-origin absolute URLs are rejected before requests are sent.