@tryghost/content-api
Official JavaScript client for Ghost's Content API, wrapping posts, pages, tags, authors, tiers, newsletters, and offers in a small promise-based interface.
Repository Health
Technical Analysis
@tryghost/content-api is the official JavaScript client for Ghost’s read-only Content API, published from the TryGhost/SDK monorepo alongside sibling packages like @tryghost/admin-api-schema, @tryghost/url-utils, and @tryghost/image-transform. It gives Node and browser applications a typed, promise-based way to fetch published content — posts, pages, tags, authors, settings, tiers, newsletters, and member offers — from any Ghost site without hand-rolling REST calls.
The client is deliberately minimal: a single factory function (GhostContentAPI) builds resource objects with browse/read methods on top of Axios, handles API versioning through an Accept-Version header (with backward-compatible support for the older /v2/–/v4/ URL-prefixed API generations), and normalizes Ghost’s error payloads into standard JS Error objects. It ships as CJS, ESM, and a UMD bundle, so the same package works in a Node backend, a bundler-based frontend build, or directly via a <script> tag.
It’s the standard way any JAMstack site, static site generator, or custom frontend built against a headless Ghost instance pulls in blog content, and it underlies Ghost’s own official starter themes and many third-party Ghost integrations.
What You Get
- GhostContentAPI factory - a single configuration call (
url,key,version) returns a ready-to-use client with no further setup. - Resource-scoped browse/read methods -
posts.browse(),authors.read({slug}), etc., cover every content type Ghost’s Content API exposes, including tiers, newsletters, and member offers. - Multi-version API support - transparently targets the current
/content/endpoint or legacy/v2/–/v4/content/prefixed endpoints via theversionoption, with deprecation warnings guiding upgrades. - Normalized error handling - Ghost’s API error responses are unpacked into real
Errorinstances withname,message, and the original response attached, instead of raw Axios rejection objects. - Three build targets - CJS, ES modules, and a minified UMD bundle (
unpkg-ready) so it drops into Node, bundler-based frontends, or a plain<script>tag alike. - Member-token support for gated content - resource methods accept a members token to fetch content gated behind Ghost’s membership/subscription system.
Common Use Cases
- Headless Ghost frontends - a Next.js, Astro, or Gatsby site fetches posts and pages from a Ghost backend at build or request time using this client instead of raw fetch calls.
- Custom Ghost themes and integrations - third-party tools that need to list posts, tags, or authors from a Ghost site without touching the Admin API.
- Static site generation pipelines - build-time scripts pull all published posts via
posts.browse()with pagination to pre-render pages. - Membership-gated content apps - applications that pass a member token to
read/browsecalls to retrieve content restricted to paying subscribers or specific tiers.
Under The Hood
Architecture
The package is a single-file library (lib/content-api.js) exporting one factory function, GhostContentAPI, that builds a resource map by reducing over a fixed list of resource types (posts, authors, tags, pages, settings, tiers, newsletters, offers) and attaching browse/read closures to each, then pruning methods that don’t apply (e.g. settings has no read, offers has no browse). All request construction funnels through one internal makeApiRequest, which resolves the correct URL prefix per API version (resolveAPIPrefix), assembles headers (API key, Accept-Version, User-Agent, optional members token), and normalizes both success responses (unwrapping single-item vs. paginated-with-meta shapes) and Ghost’s structured error payloads into plain JS Error objects. There is effectively one layer of abstraction — factory to per-resource methods to shared request/response handling — with no internal state beyond the closures captured at construction time, making the whole client easy to reason about and low-risk to change.
Tech Stack
Written in ES modules and transpiled with Babel/@rollup/plugin-babel against a preset-env browser target; the only runtime dependency is axios (pinned to 1.20.0) for making HTTP requests, injected through a swappable makeRequest option for testing or custom transport. Rollup builds three outputs (CJS via rollup-plugin-commonjs, ESM, and a Terser-minified UMD bundle with Node polyfills via rollup-plugin-polyfill-node) driven by rollup.config.js, with @rollup/plugin-json pulling the package version directly from package.json at build time. The package lives inside a Lerna-managed monorepo (TryGhost/SDK) alongside sibling Ghost tooling packages and is published independently to npm.
Code Quality
Testing is thorough relative to the package’s size: nearly 1,400 lines of Mocha/should/Sinon tests across per-API-version suites (v2–v6, canary, plus dedicated integration and unit test files), run through c8 for coverage with both text and Cobertura reporters, and gated behind posttest running ESLint against Ghost’s shared eslint-plugin-ghost config. Error handling is explicit throughout makeApiRequest, distinguishing Ghost API error responses from generic request failures and re-throwing typed errors rather than swallowing them. Input validation (URL protocol, trailing slashes, key format via regex, path leading/trailing slashes) is enforced synchronously at client construction with descriptive thrown errors, which is a stronger fail-fast posture than most thin API wrapper clients bother with.
What Makes It Unique
Rather than a generic HTTP wrapper, the client bakes in Ghost-specific API evolution handling directly into its core: it transparently bridges four generations of Ghost’s Content API URL/versioning scheme (unprefixed /content/, and the older /v2/, /v3/, /v4/, canary prefixed variants) behind one consistent version option, complete with deprecation warnings steering consumers toward the current Accept-Version header approach. Combined with first-class support for membership-gated resources (member tokens threaded through to protected read/browse calls) and tri-target bundling out of one small source file, it trades footprint for pragmatic longevity across a rapidly-versioned upstream API — a concern most one-off REST client wrappers don’t bother solving at all.