Guides

Content API

Load your content in one request, with types.

Leiho also serves your content to the front end, so WordPress is the only source of truth and the front-end repository holds code, not content.

ts
// astro.config / build script — Node 18+
import { loadLeihoContent, indexLeihoContent } from 'leiho/content';
import type { LeihoExport, Page } from './src/leiho'; // npx leiho types

const { data } = await loadLeihoContent( { url: 'https://cms.example.com' } );
const content = indexLeihoContent( data as LeihoExport );
const home = content.byPath( '/' ) as Page;
home.fields.hero_title; // typed
Endpoint Returns
GET /wp-json/leiho/v1/export Every published item of the Leiho content types, in one response. Cached until something changes, with an ETag (an unchanged build gets a 304).
GET /wp-json/leiho/v1/content?type=&path=&slug= A filtered, paginated list.
GET /wp-json/leiho/v1/content/{id} One item (drafts for logged-in editors).
GET /wp-json/leiho/v1/types TypeScript declarations of exactly that JSON, generated from your ACF field groups.

Each item carries id, type, slug, path (the front-end path from your URL settings), title, dates, excerpt, rendered content, featured_image, terms and fields. Fields are keyed by name and already shaped for a front end: an image is { id, url, alt, width, height, sizes }, a relation is { id, type, slug, title, path }, a WYSIWYG is rendered HTML, a flexible content row has a _layout.

  • One request per build instead of one per page: no more "Error establishing a database connection" on shared hosting.
  • Snapshot fallback: every successful load is written to .leiho/content.json. If WordPress doesn't answer, the build carries on with the last known content and says so (fallback: 'error' to fail instead).
  • Types: npx leiho types --url https://cms.example.com writes src/leiho.d.ts. Regenerate it when you change fields.
  • Access: public by default (published content only, which is public anyway). Switch to Restricted in the settings and set LEIHO_TOKEN in the build environment.
  • Caching: Leiho plugs into the caches the site already has (Settings → Content API → Cache, on by default).
  • With a persistent object cache (Redis Object Cache, Memcached, LiteSpeed, W3 Total Cache…), the export, single items, path lookups and the types live in the WordPress object cache (group leiho). Invalidation is O(1): every change switches to a new cache generation instead of deleting keys. Without one, Leiho keeps its own copy of the export in a non-autoloaded option and writes nothing else to the database.
  • Every endpoint sends an ETag and Cache-Control: public, max-age=0, must-revalidate, and answers 304 to a matching If-None-Match. In restricted mode, for logged-in users and for drafts, responses are private (and LiteSpeed is told not to cache them).
  • When served content changes, Leiho purges the content API from the page caches it finds, once per request: LiteSpeed (by tag and URL, Hostinger included), WP Rocket and W3 Total Cache and WP Super Cache (by URL when they can), SiteGround, WP Fastest Cache, Cloudflare, Breeze, Nginx Helper. Hook leiho_purge_caches to purge anything else (a CDN, a reverse proxy); leiho_purge_urls changes the URLs.
  • Clear the content cache in the settings does all of this on demand.

Something unclear on this page? Tell us →