{"id":47338490,"url":"https://github.com/juvojustin/fluent-wp-client","last_synced_at":"2026-05-01T17:01:15.233Z","repository":{"id":343031366,"uuid":"1175960235","full_name":"JUVOJustin/fluent-wp-client","owner":"JUVOJustin","description":"Runtime-agnostic TypeScript WordPress REST API client with typed CRUD, auth, abilities, and relation helpers","archived":false,"fork":false,"pushed_at":"2026-04-29T14:56:09.000Z","size":103693,"stargazers_count":0,"open_issues_count":7,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-29T16:33:45.245Z","etag":null,"topics":["client","rest-api","wordpress"],"latest_commit_sha":null,"homepage":"https://justin-vogt.com/repo/fluent-wp-client/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JUVOJustin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-08T12:28:13.000Z","updated_at":"2026-04-29T14:54:55.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/JUVOJustin/fluent-wp-client","commit_stats":null,"previous_names":["juvojustin/fluent-wp-client"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/JUVOJustin/fluent-wp-client","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JUVOJustin%2Ffluent-wp-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JUVOJustin%2Ffluent-wp-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JUVOJustin%2Ffluent-wp-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JUVOJustin%2Ffluent-wp-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JUVOJustin","download_url":"https://codeload.github.com/JUVOJustin/fluent-wp-client/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JUVOJustin%2Ffluent-wp-client/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32505110,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["client","rest-api","wordpress"],"created_at":"2026-03-17T22:22:12.773Z","updated_at":"2026-05-01T17:01:15.200Z","avatar_url":"https://github.com/JUVOJustin.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# fluent-wp-client\n\nRuntime-agnostic TypeScript client for the WordPress REST API.\n\nWorks on **Node.js**, **Deno**, **Bun**, and in the **browser** — using only web-standard APIs (`fetch`, `URL`, `Blob`).\n\n## Install\n\n```bash\nnpm install fluent-wp-client\n```\n\n## Quick start\n\n```ts\nimport { WordPressClient } from 'fluent-wp-client';\n\nconst wp = new WordPressClient({\n  baseUrl: 'https://your-wordpress-site.com',\n});\n\nconst posts = wp.content('posts');\n\n// Read a list of posts\nconst recentPosts = await posts.list({ perPage: 10 });\n\n// First-class resources use the same fluent style\nconst comments = await wp.comments().list({ post: 42 });\n\n// Read a single post\nconst post = await posts.item('hello-world');\n\n// Create a draft post (requires auth)\nconst draft = await posts.create({ title: 'Hello', status: 'draft' });\n```\n\n## Design principles\n\n- **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.\n- **Schemas are discoverable at runtime.** Every resource and ability exposes its live JSON Schema through `.describe()` and `wp.explore()`, making the client adaptive to custom post types, taxonomies, plugin endpoints, and ACF fields.\n- **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 `supports` removing fields like `title`, `content`, or `author`.\n- **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.\n- **Runtime-agnostic and serialization-safe.** Terminal reads return plain DTOs that survive `structuredClone()`, `JSON.stringify()`, and cross-boundary transport (SSR, RSC, `postMessage`).\n\n## Features\n\n- **Unified typed content builders** — `content('posts')`, `content('pages')`, `content('books')`, and `terms('genre')` share one API shape, with stricter typing for built-in resources\n- **First-class fluent resource clients** — `media()`, `comments()`, `users()`, and `settings()` expose consistent `list/item/create/update/delete/describe` APIs, with `upload()` for media and `me()` for users\n- **Cross-resource search** — `searchContent()` queries across posts, pages, and CPTs via the `/wp/v2/search` endpoint\n- **Parallel bulk fetching** — `listAll()` fetches all pages in parallel batches for dramatic speed improvements on large datasets\n- **Rate limiting support** — `onRequest` callback lets you implement custom rate limiting or request logging\n- **Extensible collection filters** — built-in list helpers and generic resource builders accept typed core filters plus extra endpoint-specific query params\n- **Lean embedded payloads** — post-like DTO reads skip `_embed` by default; opt in with `embed: true` or selective `embed: ['author', 'wp:term']` and use typed extraction helpers\n- **Flexible CPT defaults** — generic content reads tolerate post types that omit `title`, `content`, `excerpt`, or `author`\n- **Portable Gutenberg block add-on** — `fluent-wp-client/blocks` adds block type discovery, generated block JSON Schemas, parse/serialize/validate helpers, and explicit `.blocks().get()` / `.blocks().set()` content workflows\n- **AI SDK tool factories** — `fluent-wp-client/ai-sdk` exposes manually composable Vercel AI SDK tools for content reads, mutations, abilities, and block workflows\n- **CLI schema/code generation** — `fluent-wp-client` ships a CLI for discovering resource schemas and generating TypeScript, JSON Schema, and Zod outputs\n- **Auth flexibility** — Basic auth (application passwords), JWT, cookie+nonce, prebuilt headers, and per-request signing\n- **WordPress Abilities API** — discover and execute registered abilities with the same upstream-validated model as the rest of WordPress\n- **Schema discovery and custom validation** — validator-agnostic root exports plus `.describe()`, `.explore()`, and CLI schema generation for app-level Zod, Valibot, or custom validation\n- **Embed extraction helpers** — `getEmbeddedAuthor()`, `getEmbeddedTerms()`, `getEmbeddedFeaturedMedia()`, `getEmbeddedParent()`, plus ACF helpers like `getAcfFieldPosts()` and `getAcfFieldTerms()`\n\n## Gutenberg block workflows\n\nUse the dedicated block add-on when you need block parsing, validation, or block type discovery:\n\n```ts\nimport { WordPressClient } from 'fluent-wp-client';\nimport { withBlocks } from 'fluent-wp-client/blocks';\n\nconst wp = new WordPressClient({\n  baseUrl: 'https://example.com',\n  auth: { username: 'admin', password: 'app-password' },\n});\n\nconst wpBlocks = withBlocks(wp);\nconst blockSchemas = await wpBlocks.blocks().schemas();\nconst postQuery = wpBlocks.content('posts').item('hello-world');\nconst blocks = await postQuery.blocks().get({\n  schemas: blockSchemas,\n  validate: true,\n});\n\nblocks[0].innerHTML = '\u003cp\u003eUpdated paragraph body.\u003c/p\u003e';\nblocks[0].innerContent = ['\u003cp\u003eUpdated paragraph body.\u003c/p\u003e'];\n\nawait postQuery.blocks().set(blocks, blockSchemas);\n```\n\nStandalone helpers are also available when you already have raw `content.raw`:\n\n```ts\nimport {\n  createWordPressBlockJsonSchemas,\n  parseWordPressBlocks,\n  serializeWordPressBlocks,\n  validateWordPressBlocks,\n} from 'fluent-wp-client/blocks';\n\nconst blockTypes = await wpBlocks.blocks().list();\nconst blockSchemas = createWordPressBlockJsonSchemas(blockTypes);\nconst blocks = await parseWordPressBlocks(content.raw);\nconst result = await validateWordPressBlocks(blocks, {\n  schemas: blockSchemas,\n});\n\nif (result.valid) {\n  const nextContent = await serializeWordPressBlocks(blocks);\n}\n```\n\n`blocks().set()` does not implicitly fetch block types. Pass the schemas you want to allow when you need whitelist validation.\n\nIf you want native Zod schemas for AI-produced block trees, import them from `fluent-wp-client/blocks/zod`.\n\n## AI SDK Integration\n\nUse the optional `fluent-wp-client/ai-sdk` entrypoint when you want manually composable [Vercel AI SDK](https://sdk.vercel.ai/) tools built on top of your WordPress client instance.\n\n```ts\nimport { WordPressClient } from 'fluent-wp-client';\nimport {\n  getContentCollectionTool,\n  getContentTool,\n  saveContentTool,\n} from 'fluent-wp-client/ai-sdk';\n\nconst wp = new WordPressClient({\n  baseUrl: 'https://example.com',\n  auth: { username: 'editor', password: 'app-password' },\n});\n\nawait wp.explore();\n\nconst tools = {\n  searchPosts: getContentCollectionTool(wp, {\n    contentType: 'posts',\n    fixedArgs: { perPage: 5, status: 'publish' },\n  }),\n  readContent: getContentTool(wp),\n  writePost: saveContentTool(wp, {\n    contentType: 'posts',\n    fixedInput: { status: 'draft' },\n  }),\n};\n```\n\n`saveContentTool` handles both create and update — call it without `id` to create, or with `id` to update. One tool surface, one mental model.\n\nEvery 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.\n\nUse `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.\n\nSee `docs/ai-sdk.mdx` for the full tool catalog and configuration model.\n\n## CLI\n\nUse the built-in CLI to discover WordPress REST schemas and generate code artifacts:\n\n```bash\nnpx fluent-wp-client schemas --url https://example.com\nnpx fluent-wp-client schemas --url https://example.com --types-out wp-types.d.ts\nnpx fluent-wp-client schemas --url https://example.com --zod-out wp-schemas.mjs --types-out wp-types.d.ts\nnpx fluent-wp-client schemas --url https://example.com --zod-out wp-schemas.ts --json-out wp-schemas.json --types-out wp-types.d.ts\n```\n\nThe `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`.\n\n## Rate limiting and request control\n\nUse the `onRequest` callback to implement rate limiting, request logging, or other custom logic:\n\n```ts\n// Simple delay between requests\nconst wp = new WordPressClient({\n  baseUrl: 'https://example.com',\n  onRequest: async (url, init) =\u003e {\n    await new Promise(resolve =\u003e setTimeout(resolve, 100));\n  },\n});\n```\n\n```ts\n// Token bucket rate limiter\nconst rateLimiter = new TokenBucketLimiter({ tokensPerSecond: 10, maxTokens: 20 });\nconst wpWithRateLimit = new WordPressClient({\n  baseUrl: 'https://example.com',\n  onRequest: async (url, init) =\u003e {\n    await rateLimiter.acquireToken();\n  },\n});\n```\n\n## Parallel bulk fetching\n\n`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:\n\n```ts\n// Fetch all posts with default concurrency (5 pages at a time)\nconst allPosts = await wp.content('posts').listAll();\n\n// Higher concurrency for faster fetching (use with caution)\nconst allUsers = await wp.users().listAll({}, {}, { concurrency: 10 });\n\n// Lower concurrency to be gentler on the server\nconst allComments = await wp.comments().listAll({}, {}, { concurrency: 2 });\n```\n\nThe `explore()` API also makes parallel requests when discovering all resources, dramatically speeding up schema discovery.\n\n## Flexible collection filters\n\nCollection 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.\n\n```ts\nconst posts = await wp.content('posts').list({\n  search: 'hello world',\n  include: [4, 8],\n  titleSearch: 'Hello',\n});\n\nconst books = await wp.content('books').list({\n  include: [164, 165],\n  titleSearch: 'Test Book',\n});\n```\n\nArray params are serialized as repeated `param[]` entries instead of comma-joined strings.\n\n## Embedded data\n\nPost-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:\n\n```ts\nimport { getEmbeddedAuthor, getEmbeddedTerms } from 'fluent-wp-client';\n\nconst post = await wp.content('posts').item('hello-world', { embed: true });\nconst author = getEmbeddedAuthor(post);\nconst categories = getEmbeddedTerms(post, 'category');\n\n// Selective embed reduces server-side work\nconst post2 = await wp.content('posts').item('hello-world', {\n  embed: ['author', 'wp:term'],\n});\n\n// Embed on lists\nconst posts = await wp.content('posts').list({ perPage: 10, embed: true });\n```\n\n## Auth examples\n\n```ts\n// Basic auth (WordPress application passwords)\nconst wp = new WordPressClient({\n  baseUrl: 'https://example.com',\n  auth: { username: 'admin', password: 'app-password' },\n});\n\n// JWT auth\nconst wp = new WordPressClient({\n  baseUrl: 'https://example.com',\n  auth: { token: 'jwt-token' },\n});\n\n// Cookie + nonce (browser sessions)\nconst wp = new WordPressClient({\n  baseUrl: 'https://example.com',\n  auth: { nonce: window.wpApiSettings.nonce, credentials: 'include' },\n});\n```\n\n## Documentation\n\nFull documentation lives in the [`docs/`](./docs/) folder:\n\n- [Overview](./docs/index.mdx) — feature overview and quick start\n- [Usage guide](./docs/usage.mdx) — all client methods, CRUD patterns, pagination, and error handling\n- [Authentication](./docs/auth.mdx) — auth strategies and resolver helpers\n- [Gutenberg content](./docs/gutenberg-content.mdx) — the `fluent-wp-client/blocks` add-on for block type discovery, parsing, validation, and writes\n- [Custom endpoints](./docs/custom-endpoints.mdx) — custom post types, taxonomies, and plugin namespaces\n- [Abilities](./docs/abilities.mdx) — WordPress Abilities API\n- [Validation](./docs/validation.mdx) — Standard Schema, Zod, and custom validators\n- [Embed extraction](./docs/usage.mdx#embed-and-extraction-helpers) — embed parameters, typed extraction helpers, and ACF relation extraction\n- [Schema discovery](./docs/schema-discovery.mdx) — `.describe()`, `.explore()`, and runtime Zod conversion\n- [CLI](./docs/cli.mdx) — generate JSON Schema and Zod artifacts from live WordPress instances\n- [AI SDK integration](./docs/ai-sdk.mdx) — compose WordPress tools for the Vercel AI SDK\n\n## Development\n\n```bash\n# Start the local WordPress environment\nnpm run wp:start\n\n# Run the integration test suite\nnpm test\n\n# Stop the environment\nnpm run wp:stop\n```\n\nCore 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`.\n\nTests run against a real WordPress Docker container managed by [`@wordpress/env`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/). See [`tests/`](./tests/) for setup details.\n\n## License\n\nGPL-2.0-or-later\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuvojustin%2Ffluent-wp-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuvojustin%2Ffluent-wp-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuvojustin%2Ffluent-wp-client/lists"}