contentful.js
The official JavaScript/TypeScript SDK for Contentful's Content Delivery and Preview APIs, with built-in retries, sync, and cursor pagination.
Repository Health
Technical Analysis
contentful.js is the official JavaScript client for Contentful’s Content Delivery and Content Preview APIs, giving Node.js, browser, and React Native applications a typed interface for fetching entries, assets, content types, and sync data from a Contentful space. It wraps the underlying REST API with request building, retry logic for rate limits and server errors, and automatic link resolution so that referenced entries and assets are inlined into the response tree.
Beyond basic fetching, the library supports environments, locale-aware queries (single locale or withAllLocales), cursor-based pagination for large collections, the Sync API for incremental content updates, and Timeline Preview for querying content by future release date. It ships as ESM with CJS and browser bundles, is written entirely in TypeScript with full type definitions, and layers content-source-maps support for Contentful’s visual editing tools.
What You Get
- Typed client -
createClient()returns a fully-typedContentfulClientApicovering entries, assets, content types, tags, and concepts. - Chain modifiers -
.withAllLocales,.withoutLinkResolution,.withoutUnresolvableLinks, and.withLocaleBasedPublishingcompose without re-instantiating the client. - Built-in resilience - automatic retry on 429/500 responses with configurable retry limits and timeouts.
- Multiple build targets - ESM, CJS, and a minified browser bundle distributed via jsDelivr/unpkg.
Common Use Cases
- Static site generation - fetch entries and assets at build time for Next.js/Astro/Gatsby sites backed by Contentful.
- Content preview - point the client at the Preview API host to render draft content before publishing.
- Incremental sync - use the Sync API to keep a local cache or search index up to date without re-fetching everything.
- Multi-locale delivery - request all locales at once with
withAllLocalesfor internationalized frontends.
Under The Hood
Architecture
contentful.js is organized as a thin, functional layered client: lib/contentful.ts exposes createClient(), which builds an axios-based HTTP client via contentful-sdk-core’s createHttpClient, then delegates to lib/make-client.ts’s makeClient() to construct the actual API surface. make-client.ts implements chain modifiers (withAllLocales, withoutLinkResolution, withoutUnresolvableLinks, withLocaleBasedPublishing) by re-invoking create-contentful-api.ts’s createContentfulApi() with different ChainOptions, using Object.defineProperty getters so each modifier returns a fresh client rather than mutating shared state. create-contentful-api.ts is the actual request/response layer — it builds request configs, calls errorHandler from contentful-sdk-core, resolves circular/linked references via utils/resolve-circular.ts, and normalizes search, selection, and cursor-pagination parameters through a set of single-purpose utils/normalize-*.ts modules. Sync is split out into lib/paged-sync.ts. The layering is clean and the abstractions (options → HTTP → API surface → response normalization) are easy to trace, though the chain-modifier pattern trades some type-system straightforwardness for ergonomics.
Tech Stack
The library is written in TypeScript, compiled with tsc to ESM and bundled with Rollup (rollup.config.js) into a CJS build (dist/contentful.cjs) and browser bundles, with @rollup/plugin-terser for minification and rollup-plugin-visualizer for bundle analysis; size-limit enforces bundle-size budgets in CI. HTTP requests go through axios, wrapped by Contentful’s own contentful-sdk-core package for auth headers, retries, and error normalization. Runtime dependencies are deliberately small — @contentful/rich-text-types, @contentful/content-source-maps, contentful-resolve-response, json-stringify-safe, tslib, and type-fest — with everything else (ESLint, Prettier, Vitest, tsd for type-testing, Husky + lint-staged, semantic-release) confined to devDependencies. Node.js >=18 and modern browsers (Chrome/Firefox/Edge/Safari, React Native via Metro) are the supported targets per package.json’s engines/browserslist.
Code Quality
Testing is thorough: test/unit has over a dozen spec files covering the client builder, chained modifiers, cursor pagination normalization, timeline preview, and every utils/normalize-*/validate-* helper, run via Vitest with a separate test/integration suite and test/types tsd type-assertions. ESLint and Prettier are enforced via lint-staged/Husky pre-commit hooks, and a test:prepush script runs build + unit tests + type tests before allowing a push. Errors are modeled explicitly with typed classes (e.g. NotFoundError carrying a structured sys/details payload) rather than thrown strings, and parameter validation is centralized in dedicated validate-params/validate-search-parameters modules with a ValidationError type. CI runs separate build, lint, unit+integration test, type-check, and CodeQL security-scanning workflows on every push.
API Design
The chain-modifier API (.withAllLocales, .withoutLinkResolution, .withoutUnresolvableLinks, .withLocaleBasedPublishing) is the library’s signature ergonomic choice: rather than exposing a pile of boolean flags on every call, options compose as chained getters off the base client, discoverable via autocomplete and fully reflected in the TypeScript return type. Getting started requires only a space and accessToken passed to createClient(), and the same client transparently supports the Preview API via a host swap. Cursor-based pagination, the Sync API, and Timeline Preview are the genuinely CMS-specific surface beyond a typical REST wrapper — the rest is a well-executed but conventional typed-client pattern rather than a novel approach.