npm package: @se-studio/wordpress-rest-api
Description: Type-safe WordPress REST API client for Next.js headless sites with ACF field support
Latest version: 1.0.6

# @se-studio/wordpress-rest-api

Type-safe client for the WordPress REST API (`/wp-json/wp/v2`) for Next.js headless sites. Maps WordPress **pages**, **posts**, **categories**, **tags**, and **users** into `@se-studio/core-data-types` models used by `@se-studio/core-ui`.

## Environment variables

| Variable | Required | Description |
|----------|----------|-------------|
| `WORDPRESS_URL` | Yes | Origin of the WordPress site (no trailing slash), e.g. `https://cms.example.com` |
| `WORDPRESS_USERNAME` | For preview | WP user for Application Password auth |
| `WORDPRESS_APPLICATION_PASSWORD` | For preview | Application password (WP Admin → Users → Application Passwords) |

Preview mode adds `status[]=draft|pending|publish` and uses HTTP Basic auth.

## Caching and revalidation

- All fetch helpers accept the same **CMS fetch shape** as Contentful (`preview`, `cache`, `next.revalidate`, `next.tags`) via `mapCmsFetchOptionsToWordPress()` (see `fetchOptions.ts`).
- Cache tag strings are centralized in `cacheTags.ts` and must match `/api/revalidate` webhook handling (`revalidation/handler.ts`).
- Webhook payloads should include `post_type` (`post`, `page`, `category`, `post_tag`, `tag`, `user`, `author`, `banner`) and `slug` where applicable.

## ACF (Advanced Custom Fields)

The reference Docker setup (`infra/wordpress`) registers a **sections** repeater on pages and posts. Optional **SEO** fields on the same field group:

- `seo_indexed` (boolean, default true) — maps to link `indexed` / sitemap inclusion.
- `seo_hidden` (boolean) — maps to `hidden`; production renders treat hidden as `notFound` when used with app-level `hideIfHidden` (see `example-wordpress`).

## Optional WordPress features

| Feature | REST / setup |
|---------|----------------|
| **Site banners** | Custom post type `banner` with ACF fields (see `api/banners.ts`). If the CPT is missing, banner fetch returns `[]`. |
| **404 / CMS templates** | A page with slug `404` (or `template-{label}` for other labels) loaded via `wordpressTemplateRest`. |
| **Custom type index** | Implemented as a normal **page** per slug via `wordpressCustomTypeRest`. |

## Limitations vs Contentful

- No typed reference graph or deep `include` resolution; use `_embed` or extra requests.
- Rich text is **HTML** from WordPress, not Contentful Rich Text AST.
- Live preview overlays in Contentful (`data-contentful-*`) have no native WordPress equivalent; apps can leave preview helpers as no-ops.

## App helper parity with Contentful

Contentful apps use `createAppHelpers` from `@se-studio/core-ui`. The WordPress reference app implements the **same behaviors** ( `buildFetchOptions`, production `hideIfHidden`, `allowUnindexed` on link lists, related articles/people, breadcrumbs, banners, templates, custom types) in [`apps/example-wordpress/src/lib/cms-server.ts`](../../apps/example-wordpress/src/lib/cms-server.ts). A future refactor may extract a `createWordPressAppHelpers` factory into `core-ui` once more than one WordPress app needs it.

## Related packages

- `@se-studio/markdown-renderer` — `WordPressMarkdownExporter` for markdown export and search indexing.
- `@se-studio/search` — `rebuildSearchIndexWordPress()` for Upstash indexing.