npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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-client

Features

  • 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() and getAllAssets() 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 BlogPost

Error 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-id

NeverHub 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

License

Copyright (c) 2025 Bernier LLC. See LICENSE.md for details.