@bernierllc/contentful-cma-client
v1.0.2
Published
Thin wrapper around contentful-management SDK with consistent error handling, logging, and typed interfaces
Downloads
86
Readme
@bernierllc/contentful-cma-client
Thin wrapper around the official contentful-management npm package, providing consistent error handling, logging, and typed interfaces for all CMA (Content Management API) operations.
Installation
npm install @bernierllc/contentful-cma-clientFeatures
- ✅ Typed Interfaces - Full TypeScript support using
@bernierllc/contentful-types - ✅ Comprehensive CRUD - Entries, Assets, Content Types operations
- ✅ Bulk Operations - Batch create, update, publish, delete
- ✅ Automatic Pagination -
getAllEntries()andgetAllAssets()handle pagination automatically - ✅ Consistent Logging - Integrated with
@bernierllc/logger - ✅ Error Handling - Graceful error handling with detailed logging
- ✅ Publishing Workflow - Publish, unpublish, archive, unarchive support
Usage
Basic Setup
import { ContentfulCMAClient } from '@bernierllc/contentful-cma-client';
const client = new ContentfulCMAClient({
accessToken: 'your-cma-token',
spaceId: 'your-space-id',
environmentId: 'master' // optional, defaults to 'master'
});Entry Operations
Get Entry
const entry = await client.getEntry<BlogPost>('entry-id');
console.log(entry.fields.title);Get Entries with Query
const entries = await client.getEntries<BlogPost>({
content_type: 'blogPost',
limit: 10,
skip: 0
});Get All Entries (with automatic pagination)
const allEntries = await client.getAllEntries<BlogPost>({
content_type: 'blogPost'
});Create Entry
const newEntry = await client.createEntry<BlogPost>(
'blogPost',
{
title: { 'en-US': 'My Blog Post' },
body: { 'en-US': 'Content here...' }
}
);Create Entry with Custom ID
const newEntry = await client.createEntry<BlogPost>(
'blogPost',
{
title: { 'en-US': 'My Blog Post' }
},
{ entryId: 'custom-entry-id' }
);Update Entry
const updatedEntry = await client.updateEntry<BlogPost>(
'entry-id',
{
title: { 'en-US': 'Updated Title' }
},
2 // version number
);Publish Entry
const publishedEntry = await client.publishEntry('entry-id', 2);Unpublish Entry
await client.unpublishEntry('entry-id');Archive Entry
await client.archiveEntry('entry-id', 2);Unarchive Entry
await client.unarchiveEntry('entry-id');Delete Entry
await client.deleteEntry('entry-id');Asset Operations
Get Asset
const asset = await client.getAsset('asset-id');
console.log(asset.fields.file['en-US'].url);Create Asset
const newAsset = await client.createAsset({
title: { 'en-US': 'My Image' },
file: {
'en-US': {
url: 'https://example.com/image.jpg',
fileName: 'image.jpg',
contentType: 'image/jpeg',
details: { size: 12345 }
}
}
});Process Asset (after upload)
await client.processAsset('asset-id', 1, 'en-US');Publish Asset
const publishedAsset = await client.publishAsset('asset-id', 2);Delete Asset
await client.deleteAsset('asset-id');Content Type Operations
Get Content Type
const contentType = await client.getContentType('blogPost');
console.log(contentType.fields);Get All Content Types
const contentTypes = await client.getContentTypes();Bulk Operations
Bulk Create Entries
const result = await client.bulkCreateEntries([
{ contentTypeId: 'blogPost', fields: { title: { 'en-US': 'Post 1' } } },
{ contentTypeId: 'blogPost', fields: { title: { 'en-US': 'Post 2' } } }
]);
console.log(`Created: ${result.successful.length}`);
console.log(`Failed: ${result.failed.length}`);
// Handle failures
result.failed.forEach(failure => {
console.error(`Failed to create:`, failure.error);
});Bulk Publish Entries
const result = await client.bulkPublishEntries([
{ entryId: 'entry-1', version: 2 },
{ entryId: 'entry-2', version: 1 }
]);Bulk Delete Entries
const result = await client.bulkDeleteEntries([
'entry-1',
'entry-2',
'entry-3'
]);Configuration Options
interface ContentfulCMAConfig {
accessToken: string; // Required: CMA access token
spaceId: string; // Required: Contentful space ID
environmentId?: string; // Optional: defaults to 'master'
host?: string; // Optional: defaults to 'api.contentful.com'
retryOnError?: boolean; // Optional: defaults to true
timeout?: number; // Optional: defaults to 30000ms
}Query Options
interface CMAQueryOptions {
skip?: number; // Number of items to skip
limit?: number; // Number of items to return
order?: string; // Sort order (e.g., '-sys.createdAt')
locale?: string; // Locale filter
content_type?: string; // Content type filter
[key: string]: any; // Additional Contentful query params
}Type Safety
This package uses types from @bernierllc/contentful-types:
import type {
ContentfulEntry,
ContentfulAsset,
ContentfulContentType
} from '@bernierllc/contentful-types';
interface BlogPost {
title: { [locale: string]: string };
body: { [locale: string]: string };
author: { [locale: string]: ContentfulLink<'Entry'> };
}
const entry = await client.getEntry<BlogPost>('entry-id');
// entry.fields is typed as BlogPostError Handling
All methods log errors and throw them for you to handle:
try {
const entry = await client.getEntry('non-existent-id');
} catch (error) {
console.error('Failed to get entry:', error);
}Bulk operations return both successful and failed operations:
const result = await client.bulkCreateEntries(entries);
// Process successful
result.successful.forEach(entry => {
console.log('Created:', entry.sys.id);
});
// Handle failures
result.failed.forEach(({ item, error }) => {
console.error('Failed to create:', error);
});Logging
This package uses @bernierllc/logger for consistent logging:
- info: Operation completions, initialization
- debug: Request/response details, operation start
- error: Failures with context
All logs include context (spaceId, environmentId, operation details).
MECE Principles
This package follows MECE (Mutually Exclusive, Collectively Exhaustive) architecture:
Includes:
- ✅ Content Management API operations (CMA)
- ✅ CRUD for Entries, Assets, Content Types
- ✅ Publishing workflow operations
- ✅ Bulk operations
Excludes:
- ❌ Content Delivery API (use
@bernierllc/contentful-cda-client) - ❌ GraphQL queries (use
@bernierllc/contentful-graphql-client) - ❌ Webhook handling (use
@bernierllc/contentful-webhook-handler) - ❌ OAuth/Auth management (use
@bernierllc/contentful-auth)
API
ContentfulCMAClient
Main client class for interacting with Contentful Content Management API.
Constructor:
new ContentfulCMAClient(config: ContentfulCMAConfig)Entry Methods:
getEntry<T>(entryId: string): Promise<ContentfulEntry<T>>getEntries<T>(query?: CMAQueryOptions): Promise<ContentfulCollection<ContentfulEntry<T>>>getAllEntries<T>(query?: CMAQueryOptions): Promise<ContentfulEntry<T>[]>createEntry<T>(contentTypeId: string, fields: T, options?: CreateEntryOptions): Promise<ContentfulEntry<T>>updateEntry<T>(entryId: string, fields: Partial<T>, version: number): Promise<ContentfulEntry<T>>publishEntry(entryId: string, version: number): Promise<ContentfulEntry>unpublishEntry(entryId: string): Promise<ContentfulEntry>archiveEntry(entryId: string, version: number): Promise<ContentfulEntry>unarchiveEntry(entryId: string): Promise<ContentfulEntry>deleteEntry(entryId: string): Promise<void>
Asset Methods:
getAsset(assetId: string): Promise<ContentfulAsset>getAssets(query?: CMAQueryOptions): Promise<ContentfulCollection<ContentfulAsset>>getAllAssets(query?: CMAQueryOptions): Promise<ContentfulAsset[]>createAsset(fields: AssetFields, options?: CreateAssetOptions): Promise<ContentfulAsset>processAsset(assetId: string, version: number, locale: string): Promise<ContentfulAsset>publishAsset(assetId: string, version: number): Promise<ContentfulAsset>deleteAsset(assetId: string): Promise<void>
Content Type Methods:
getContentType(contentTypeId: string): Promise<ContentfulContentType>getContentTypes(): Promise<ContentfulCollection<ContentfulContentType>>
Bulk Methods:
bulkCreateEntries<T>(entries: BulkCreateEntry<T>[]): Promise<BulkOperationResult<ContentfulEntry<T>>>bulkPublishEntries(entries: BulkPublishEntry[]): Promise<BulkOperationResult<ContentfulEntry>>bulkDeleteEntries(entryIds: string[]): Promise<BulkOperationResult<void>>bulkCreateAssets(assets: AssetFields[]): Promise<BulkOperationResult<ContentfulAsset>>
Integrations
Logger Integration
This package integrates with @bernierllc/logger for consistent logging across all operations. The logger is initialized automatically and provides:
- Structured logging with operation context (spaceId, environmentId, entryId)
- Log levels: info, debug, error
- Automatic error tracking with full error details
// Logger is automatically initialized
const client = new ContentfulCMAClient(config);
// Logs are emitted for all operations
await client.getEntry('entry-id');
// Logs: [INFO] Fetching entry entry-id from space xxx-space-idNeverHub Integration
This package does not require NeverHub integration as it is a core client library. However, services using this client can integrate with @bernierllc/neverhub-adapter for event tracking:
import { ContentfulCMAClient } from '@bernierllc/contentful-cma-client';
import { NeverHubAdapter } from '@bernierllc/neverhub-adapter';
class ContentfulService {
private client: ContentfulCMAClient;
private neverhub?: NeverHubAdapter;
async initialize() {
this.client = new ContentfulCMAClient(config);
// Optional NeverHub integration for event tracking
if (await NeverHubAdapter.detect()) {
this.neverhub = new NeverHubAdapter();
await this.neverhub.register({
type: 'contentful-service',
capabilities: ['content-management']
});
}
}
async createEntry<T>(contentTypeId: string, fields: T) {
const entry = await this.client.createEntry(contentTypeId, fields);
// Track event in NeverHub (if available)
if (this.neverhub) {
await this.neverhub.logEvent({
type: 'content.created',
data: { entryId: entry.sys.id, contentType: contentTypeId }
});
}
return entry;
}
}Integration Status:
- ✅ Logger integration: Required (built-in)
- ⚪ NeverHub integration: Optional (consumer responsibility)
- ✅ Graceful degradation: Works standalone without external services
Related Packages
- @bernierllc/contentful-types - TypeScript types
- @bernierllc/contentful-cda-client - Content Delivery API
- @bernierllc/contentful-graphql-client - GraphQL API
- @bernierllc/contentful-gateway-service - Unified gateway
License
Copyright (c) 2025 Bernier LLC. See LICENSE.md for details.
