Content Delivery API (CDA) client wrapper with Sync API support for published content retrieval
npm install @bernierllc/contentful-cda-client> Content Delivery API (CDA) client wrapper for published content retrieval and sync operations.


Wrapper around the official contentful npm package for Content Delivery API access. Provides published content retrieval, Sync API for incremental updates, and CDN-backed queries with high rate limits.
- ✅ Published Content Access - Fetch entries and assets from CDN
- ✅ Sync API Support - Initial sync + delta sync for incremental updates
- ✅ Locale-Aware - Fetch content in specific languages
- ✅ Include Depth - Control reference resolution depth
- ✅ High Rate Limits - CDN-backed queries (55 requests/sec)
- ✅ Type-Safe - Full TypeScript support with strict typing
- ✅ Logger Integration - Built-in structured logging
- ❌ Draft/unpublished content (use @bernierllc/contentful-cma-client)
- ❌ Content modification (use @bernierllc/contentful-cma-client)
- ❌ GraphQL queries (use @bernierllc/contentful-graphql-client)
``bash`
npm install @bernierllc/contentful-cda-client
`typescript
import { ContentfulCDAClient } from '@bernierllc/contentful-cda-client';
// Initialize client
const client = new ContentfulCDAClient({
space: 'your-space-id',
accessToken: 'your-cda-token', // CDA token (not CMA token!)
environment: 'master'
});
// Fetch single entry
const entry = await client.getEntry('entryId');
// Fetch entries with query
const posts = await client.getEntries({
content_type: 'blogPost',
limit: 10,
order: '-sys.createdAt'
});
// Initial sync
const syncResult = await client.initialSync();
console.log(Synced ${syncResult.entries.length} entries);
// Delta sync (incremental updates)
const deltaResult = await client.deltaSync(syncResult.nextSyncToken);
console.log(Updated ${deltaResult.entries.length} entries);`
#### new ContentfulCDAClient(config)
Creates a new CDA client instance.
`typescript`
interface ContentfulCDAConfig {
space: string; // Required: Contentful space ID
accessToken: string; // Required: CDA access token
environment?: string; // Optional: Environment (default: 'master')
host?: string; // Optional: API host (default: 'cdn.contentful.com')
retryOnError?: boolean; // Optional: Retry on error (default: true)
timeout?: number; // Optional: Request timeout (default: 30000ms)
}
Example:
`typescript`
const client = new ContentfulCDAClient({
space: 'abc123xyz',
accessToken: 'your-cda-token',
environment: 'master',
timeout: 60000
});
#### getEntry
Retrieves a single published entry by ID.
`typescript
interface BlogPost {
title: string;
body: string;
author: ContentfulLink<'Entry'>;
}
const post = await client.getEntry
console.log(post.fields.title);
// Fetch in specific locale
const spanishPost = await client.getEntry
`
#### getEntries
Retrieves multiple published entries based on query parameters.
`typescript
// Fetch all blog posts
const posts = await client.getEntries({
content_type: 'blogPost'
});
// Fetch with pagination
const entries = await client.getEntries({
content_type: 'article',
limit: 10,
skip: 20,
order: '-sys.createdAt',
include: 2 // Include depth for references
});
// Fetch in specific locale
const frenchArticles = await client.getEntries({
content_type: 'article',
locale: 'fr-FR'
});
// Filter by field values
const featuredPosts = await client.getEntries({
content_type: 'blogPost',
'fields.featured': true,
limit: 5
});
`
Query Parameters:
- content_type: Filter by content type IDlimit
- : Number of items to return (default: 100, max: 1000)skip
- : Number of items to skip (for pagination)order
- : Sort order (e.g., sys.createdAt, -sys.updatedAt)include
- : Include depth for linked entries/assets (0-10)locale
- : Fetch content in specific localefields.[fieldName]
- : Filter by field valuesselect
- : Select specific fields to return
#### getAsset(assetId, locale?)
Retrieves a single published asset by ID.
`typescript`
const asset = await client.getAsset('1MvhB4KOyyCMIo0KeeqUaa');
console.log(asset.fields.file['en-US'].url); // Image URL
console.log(asset.fields.file['en-US'].details.size); // File size
#### getAssets(query?)
Retrieves multiple published assets based on query parameters.
`typescript
// Fetch all images
const images = await client.getAssets({
mimetype_group: 'image',
limit: 20
});
// Fetch with pagination and sorting
const assets = await client.getAssets({
limit: 50,
skip: 0,
order: '-sys.createdAt'
});
`
#### initialSync(options?)
Performs an initial sync to retrieve all published content. Returns a sync token for subsequent delta syncs.
`typescriptSynced ${syncResult.entries.length} entries
// Sync all content
const syncResult = await client.initialSync();
console.log();Synced ${syncResult.assets.length} assets
console.log();
// Store the sync token
const syncToken = syncResult.nextSyncToken;
// Sync only entries (not assets)
const entrySync = await client.initialSync({ type: 'Entry' });
// Sync specific content type
const blogSync = await client.initialSync({
type: 'Entry',
content_type: 'blogPost'
});
// Sync in specific locale
const frenchSync = await client.initialSync({ locale: 'fr-FR' });
`
Options:
`typescript`
interface ContentfulSyncOptions {
type?: 'all' | 'Asset' | 'Entry' | 'Deletion' | 'DeletedAsset' | 'DeletedEntry';
content_type?: string; // Filter by content type
locale?: string; // Filter by locale
limit?: number; // Items per page
}
#### deltaSync(nextSyncToken)
Performs a delta sync using a sync token from a previous sync. Returns only changes since the last sync.
`typescript
// Perform delta sync
const deltaResult = await client.deltaSync(storedSyncToken);
// Process new/updated content
deltaResult.entries.forEach(entry => {
console.log(Updated entry: ${entry.sys.id});
});
deltaResult.assets.forEach(asset => {
console.log(Updated asset: ${asset.sys.id});
});
// Process deletions
deltaResult.deletedEntries.forEach(deleted => {
console.log(Deleted entry: ${deleted.sys.id});
});
deltaResult.deletedAssets.forEach(deleted => {
console.log(Deleted asset: ${deleted.sys.id});
});
// Store new token for next sync
storedSyncToken = deltaResult.nextSyncToken;
`
Sync Collection Response:
`typescript`
interface SyncCollection {
entries: Entry[]; // New/updated entries
assets: Asset[]; // New/updated assets
deletedEntries: Entry[]; // Deleted entries
deletedAssets: Asset[]; // Deleted assets
nextSyncToken: string; // Token for next delta sync
nextPageUrl?: string; // URL for pagination (if needed)
}
#### getConfig()
Returns the current client configuration (without exposing the access token).
`typescript`
const config = client.getConfig();
console.log(config.space); // 'your-space-id'
console.log(config.environment); // 'master'
// config.accessToken is NOT included for security
#### getRawClient()
Returns the underlying Contentful SDK client for advanced operations.
`typescript`
const rawClient = client.getRawClient();
// Use for operations not covered by the wrapper
`typescript
import { ContentfulCDAClient } from '@bernierllc/contentful-cda-client';
class ContentCache {
private client: ContentfulCDAClient;
private syncToken: string | null = null;
private cache: Map
constructor(config: ContentfulCDAConfig) {
this.client = new ContentfulCDAClient(config);
}
async initialize(): Promise
console.log('Initializing content cache...');
const syncResult = await this.client.initialSync();
// Store all entries in cache
syncResult.entries.forEach(entry => {
this.cache.set(entry.sys.id, entry);
});
// Store sync token for updates
this.syncToken = syncResult.nextSyncToken;
console.log(Cache initialized with ${syncResult.entries.length} entries);
}
async update(): Promise
if (!this.syncToken) {
throw new Error('Cache not initialized');
}
console.log('Updating cache...');
const deltaResult = await this.client.deltaSync(this.syncToken);
// Update cache with changes
deltaResult.entries.forEach(entry => {
this.cache.set(entry.sys.id, entry);
});
// Remove deleted entries
deltaResult.deletedEntries.forEach(deleted => {
this.cache.delete(deleted.sys.id);
});
// Update sync token
this.syncToken = deltaResult.nextSyncToken;
console.log(Cache updated: +${deltaResult.entries.length} entries, -${deltaResult.deletedEntries.length} deleted);
}
get(id: string): any | undefined {
return this.cache.get(id);
}
}
// Usage
const cache = new ContentCache({
space: 'your-space-id',
accessToken: 'your-cda-token'
});
await cache.initialize();
// Update cache every 5 minutes
setInterval(() => cache.update(), 5 60 1000);
`
`typescript
async function getLocalizedContent(contentType: string, locale: string) {
const client = new ContentfulCDAClient({
space: 'your-space-id',
accessToken: 'your-cda-token'
});
const entries = await client.getEntries({
content_type: contentType,
locale: locale
});
return entries;
}
// Fetch blog posts in Spanish
const spanishPosts = await getLocalizedContent('blogPost', 'es-ES');
// Fetch products in German
const germanProducts = await getLocalizedContent('product', 'de-DE');
`
`typescript
async function fetchAllEntries(contentType: string) {
const client = new ContentfulCDAClient({
space: 'your-space-id',
accessToken: 'your-cda-token'
});
let allEntries: any[] = [];
let skip = 0;
const limit = 100;
let hasMore = true;
while (hasMore) {
const entries = await client.getEntries({
content_type: contentType,
limit: limit,
skip: skip
});
allEntries = allEntries.concat(entries);
skip += limit;
hasMore = entries.length === limit;
}
return allEntries;
}
// Fetch all blog posts (handles pagination automatically)
const allPosts = await fetchAllEntries('blogPost');
console.log(Fetched ${allPosts.length} total posts);`
The Content Delivery API is served via CDN with high rate limits:
- CDA: 55 requests/second (CDN-backed)
- Preview API: 14 requests/second
For most use cases, CDA rate limits are generous. If you need higher throughput:
1. Use the Sync API for bulk operations
2. Implement client-side caching
3. Use include parameter to fetch linked content in single request
`typescript`
try {
const entry = await client.getEntry('invalid-id');
} catch (error) {
if (error.sys?.id === 'NotFound') {
console.error('Entry not found');
} else if (error.sys?.id === 'RateLimitExceeded') {
console.error('Rate limit exceeded, retry after:', error.sys.retryAfter);
} else {
console.error('Unknown error:', error);
}
}
The package includes comprehensive test coverage (90%+):
`bash`
npm test # Run tests
npm run test:coverage # Run with coverage report
npm run test:watch # Watch mode
- @bernierllc/contentful-types - Shared Contentful type definitions@bernierllc/contentful-client-core
- - Shared HTTP client utilities@bernierllc/logger
- - Structured loggingcontentful
- - Official Contentful SDK
- Shared type definitions
- @bernierllc/contentful-client-core - HTTP client core
- @bernierllc/contentful-cma-client - Management API client (draft content, modifications)
- @bernierllc/contentful-graphql-client - GraphQL API client$3
- @bernierllc/contentful-sync-service - Automated sync orchestration
- @bernierllc/contentful-cache-service` - Content caching serviceCopyright (c) 2025 Bernier LLC. See LICENSE for details.
For issues and questions, contact Bernier LLC support or file an issue in the repository.