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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@se-studio/contentful-rest-api

v1.0.107

Published

Type-safe Contentful REST API client with caching and rate limiting for Next.js applications

Readme

@se-studio/contentful-rest-api

Type-safe Contentful REST API client with caching, rate limiting, and extensible converters for Next.js applications.

Table of Contents

Features

  • 🎯 Type-safe - Full TypeScript support with generated types from your Contentful space
  • 🔄 Dual API Support - Content Delivery API (CDA) and Content Preview API (CPA)
  • Next.js Optimized - Built-in support for Next.js App Router cache tags and revalidation
  • 🔁 Retry Logic - Automatic retry with exponential backoff for failed requests
  • 🚦 Rate Limiting - Respect Contentful API rate limits with built-in rate limiter
  • 🧩 Extensible Converters - Functional composition pattern for customizing content transformations
  • 🛡️ Error Handling - Custom error types for different failure scenarios

Installation

pnpm add @se-studio/contentful-rest-api @se-studio/core-data-types contentful

Quick Start

Basic Usage

import { contentfulPageRest } from '@se-studio/contentful-rest-api';

// Fetch a page by slug
const page = await contentfulPageRest(
  {
    spaceId: process.env.CONTENTFUL_SPACE_ID!,
    accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!,
    environment: 'master'
  },
  'home',
  {
    locale: 'en-US',
    cache: {
      tags: ['home-page'],
      revalidate: 3600 // 1 hour
    }
  }
);

Preview Mode

import { contentfulPageRest } from '@se-studio/contentful-rest-api';

// Use preview API for draft content
const page = await contentfulPageRest(
  {
    spaceId: process.env.CONTENTFUL_SPACE_ID!,
    accessToken: process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN!,
  },
  'home',
  {
    preview: true, // Uses Preview API (CPA)
    cache: {
      cache: 'no-store' // Disable Next.js caching for preview content
    }
  }
);

API Reference

Client Functions

createContentfulClient(config)

Creates a Contentful Content Delivery API (CDA) client.

import { createContentfulClient } from '@se-studio/contentful-rest-api';

const client = createContentfulClient({
  spaceId: process.env.CONTENTFUL_SPACE_ID!,
  accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!,
  environment: 'master'
});

createContentfulPreviewClient(config)

Creates a Contentful Content Preview API (CPA) client.

import { createContentfulPreviewClient } from '@se-studio/contentful-rest-api';

const previewClient = createContentfulPreviewClient({
  spaceId: process.env.CONTENTFUL_SPACE_ID!,
  accessToken: process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN!,
});

Content Fetching Functions

contentfulPageRest(config, slug, options?, converter?)

Fetches a page by slug.

Parameters:

  • config: ContentfulConfig - Contentful client configuration
  • slug: string - Page slug to fetch
  • options?: FetchOptions - Optional fetch options (locale, preview, cache, retry)
  • converter?: Converter - Optional custom converter function

Returns: Promise<IPage | null>

const page = await contentfulPageRest(
  config,
  'about-us',
  {
    locale: 'en-US',
    include: 10,
    cache: { tags: ['about-page'], revalidate: 3600 }
  }
);

contentfulPageByIdRest(config, id, options?, converter?)

Fetches a page by entry ID.

Parameters:

  • config: ContentfulConfig
  • id: string - Entry ID
  • options?: FetchOptions
  • converter?: Converter

Returns: Promise<IPage>

Throws: EntryNotFoundError if entry doesn't exist

const page = await contentfulPageByIdRest(
  config,
  '5nZHNlP9rZhWvKx4w2Z8zB'
);

contentfulAllPagesRest(config, options?, converter?)

Fetches all pages from Contentful.

Parameters:

  • config: ContentfulConfig
  • options?: FetchOptions
  • converter?: Converter

Returns: Promise<IPage[]>

const pages = await contentfulAllPagesRest(
  config,
  {
    locale: 'en-US',
    cache: { tags: ['all-pages'], revalidate: 3600 }
  }
);

contentfulEntryRest<TEntry, TResult>(config, id, converter, options?)

Generic function to fetch any entry type with a custom converter.

const article = await contentfulEntryRest(
  config,
  'articleId123',
  myArticleConverter,
  { locale: 'en-US' }
);

Converter Pattern

The package uses a functional composition pattern for converting Contentful entries to your domain types.

Base Converter

import { basePageConverter } from '@se-studio/contentful-rest-api';

// Use the base converter
const page = basePageConverter(contentfulEntry);

Enhancers

Enhancers are functions that wrap converters to add additional functionality:

import {
  basePageConverter,
  withSEO,
  withSections,
  withTags,
  compose
} from '@se-studio/contentful-rest-api';

// Compose multiple enhancers
const myConverter = compose(
  withSEO,
  withSections,
  withTags
)(basePageConverter);

// Use with API functions
const page = await contentfulPageRest(config, 'home', {}, myConverter);

Custom Converters

Create your own converter enhancers:

import type { Converter, PageEntrySkeleton } from '@se-studio/contentful-rest-api';
import type { IPage } from '@se-studio/core-data-types';

function withCustomField(
  converter: Converter<PageEntrySkeleton, IPage>
): Converter<PageEntrySkeleton, IPage> {
  return (entry) => {
    const page = converter(entry);

    // Add custom logic
    return {
      ...page,
      customField: entry.fields.customField || 'default'
    };
  };
}

// Use it
const converter = withCustomField(basePageConverter);

Caching

Cache Tags

The package automatically manages cache tags for Next.js App Router. Tags follow a consistent naming convention:

  • Individual entries: {contentType}#{slug} (e.g., page#home, article#my-post)
  • Collections: {contentType} (e.g., page, article, articleType)
  • Preview mode: global (single tag for all content when preview mode is enabled)

Automatic Cache Tagging

All content fetching functions automatically apply appropriate cache tags based on the preview flag:

import { contentfulPageRest, contentfulArticleRest } from '@se-studio/contentful-rest-api';

// Production mode: tagged with ['page#home', 'page']
const page = await contentfulPageRest(config, 'home', { preview: false });

// Preview mode: tagged with ['global']
const previewPage = await contentfulPageRest(config, 'home', { preview: true });

Preview Mode

When preview: true is passed to fetch functions or revalidation:

  • All content uses the global cache tag
  • Revalidation always clears the global tag
  • No time-based revalidation

Cache Revalidation

Webhook Integration

Set up Contentful webhooks to automatically revalidate cache when content changes:

  1. Create API Route in your Next.js app:
// app/api/revalidate/route.ts
import { createRevalidationHandler } from '@se-studio/contentful-rest-api';

export const POST = createRevalidationHandler({
  secret: process.env.REVALIDATION_SECRET,
  validateSecret: true,
  preview: process.env.DRAFT_ONLY === 'true', // or from your config
});
  1. Configure Contentful Webhook:

    • URL: https://yourdomain.com/api/revalidate
    • Secret: Set REVALIDATION_SECRET environment variable
    • Triggers: Select content types to monitor (pages, articles, etc.)
  2. Required Environment Variables:

REVALIDATION_SECRET=your-webhook-secret
CONTENTFUL_MANAGEMENT_TOKEN=your-management-token
CONTENTFUL_SPACE_ID=your-space-id
CONTENTFUL_ENVIRONMENT_NAME=your-environment

Manual Revalidation

Revalidate cache tags programmatically:

'use server'

import { revalidateTags, revalidateSingleTag } from '@se-studio/contentful-rest-api';

// Revalidate multiple tags
await revalidateTags(['page#home', 'page'], 'manual revalidation');

// Revalidate single tag
await revalidateSingleTag('article#my-post', 'article updated');

Bulk Revalidation

Trigger full cache revalidation:

# Via HTTP request
curl -X POST https://yourdomain.com/api/revalidate \
  -H "REVALIDATE_ALL: true"

# Via single tag
curl -X POST https://yourdomain.com/api/revalidate \
  -H "REVALIDATION_TAG: global"

Content Type Support

The revalidation system supports these content types:

  • Pages: page (individual: page#{slug}, collection: page)
  • Articles: article (individual: article#{slug}, collection: article)
  • Article Types: articleType (individual: articleType#{slug}, collection: articleType)
  • Custom Types: customType (collection only: customType)
  • Tags: tag (individual: tag#{slug}, collection: tag)
  • People: person (individual: person#{slug}, collection: person)
  • Assets: asset (individual: asset#{id}, collection: asset)
  • Locations: location (individual: location#{slug}, collection: location)

Tag Functions

Access tag generation functions directly:

import {
  pageTag,
  articleTag,
  articleTypeTag,
  PageTag,
  ArticleTag,
  AllTags
} from '@se-studio/contentful-rest-api';

// Individual tags
const homePageTag = pageTag('home'); // 'page#home'
const blogPostTag = articleTag('my-post'); // 'article#my-post'

// Collection tags
const allPagesTag = PageTag; // 'page'
const allArticlesTag = ArticleTag; // 'article'

// All available tags
console.log(AllTags); // ['page', 'article', 'articleType', ...]

Error Handling

The package provides custom error types for different scenarios:

import {
  ContentfulError,
  RateLimitError,
  EntryNotFoundError,
  AuthenticationError,
  ValidationError,
  isRetryableError
} from '@se-studio/contentful-rest-api';

try {
  const page = await contentfulPageByIdRest(config, 'invalid-id');
} catch (error) {
  if (error instanceof EntryNotFoundError) {
    console.log('Entry not found:', error.entryId);
  } else if (error instanceof RateLimitError) {
    console.log('Rate limited, retry after:', error.retryAfter);
  } else if (isRetryableError(error)) {
    // Handle retryable errors
  }
}

Retry Configuration

Configure retry behavior for resilient API calls:

const page = await contentfulPageRest(
  config,
  'home',
  {
    retry: {
      maxRetries: 3,
      initialDelay: 1000,
      maxDelay: 30000,
      backoffMultiplier: 2
    }
  }
);

Rate Limiting

Use the built-in rate limiter to control request rates:

import { RateLimiter } from '@se-studio/contentful-rest-api';

// Create a rate limiter (5 requests per second)
const limiter = new RateLimiter(5, 1);

// Consume a token before making a request
await limiter.consume();
const page = await contentfulPageRest(config, 'home');

Type Generation

Generate TypeScript types from your Contentful space:

1. Configure Environment Variables

Create a .env.local file:

CONTENTFUL_SPACE_ID=your-space-id
CONTENTFUL_MANAGEMENT_TOKEN=your-management-token
CONTENTFUL_ENVIRONMENT_NAME=master

2. Generate Types

pnpm --filter @se-studio/contentful-rest-api generate:types

This creates src/generated/contentful-types.ts with types matching your Contentful content model.

3. Use Generated Types

import type { TypePageSkeleton } from './generated/contentful-types';
import { contentfulEntryRest } from '@se-studio/contentful-rest-api';

const page = await client.getEntry<TypePageSkeleton>('entry-id');

Configuration Options

ContentfulConfig

interface ContentfulConfig {
  spaceId: string;
  accessToken: string;
  environment?: string; // defaults to 'master'
  host?: string; // for proxies or custom endpoints
  options?: Partial<CreateClientParams>;
}

FetchOptions

interface FetchOptions {
  locale?: string;
  preview?: boolean;
  include?: number; // include depth for linked entries (default: 10)
  cache?: CacheConfig;
  retry?: RetryConfig;
}

CacheConfig

interface CacheConfig {
  tags?: string[]; // Next.js cache tags
  revalidate?: number | false; // ISR revalidation time in seconds
  cache?: 'force-cache' | 'no-store';
}

RetryConfig

interface RetryConfig {
  maxRetries?: number; // default: 3
  initialDelay?: number; // default: 1000ms
  maxDelay?: number; // default: 30000ms
  backoffMultiplier?: number; // default: 2
}

Next.js App Router Integration

Example usage in a Next.js Server Component:

// app/[slug]/page.tsx
import { contentfulPageRest } from '@se-studio/contentful-rest-api';

interface PageProps {
  params: { slug: string };
}

export default async function Page({ params }: PageProps) {
  const page = await contentfulPageRest(
    {
      spaceId: process.env.CONTENTFUL_SPACE_ID!,
      accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!,
    },
    params.slug,
    {
      cache: {
        tags: [`page:${params.slug}`],
        revalidate: 3600
      }
    }
  );

  if (!page) {
    notFound();
  }

  return (
    <div>
      <h1>{page.title}</h1>
      <p>{page.description}</p>
    </div>
  );
}

export async function generateStaticParams() {
  const pages = await contentfulAllPagesRest({
    spaceId: process.env.CONTENTFUL_SPACE_ID!,
    accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!,
  });

  return pages.map((page) => ({
    slug: page.slug,
  }));
}

API Reference

Main Exports

API Functions

  • contentfulPageRest - Fetches a page from Contentful by slug
  • contentfulArticleRest - Fetches an article by slug and article type slug
  • contentfulArticleTypeRest - Fetches an article type by slug
  • contentfulAllPagesRest - Fetches all pages from Contentful
  • contentfulAllPageLinks - Fetches all page links (metadata only)
  • contentfulAllArticleLinks - Fetches all article links (lightweight metadata only — no full content). Returns IContentfulArticleLink[] (extends IArticleLink) including title, subtitle, date, tags, articleType, featuredImage (as visual), visuals, description, and optional summary (rich text). Used for listing/browsing UIs. Exposed in apps via getAllArticleLinks from createAppHelpers.
  • getBreadcrumbLookup - Fetches a map of path → breadcrumb label for resolving breadcrumb segments from URL paths. Uses same caching as sitemap. Use with resolveBreadcrumbSegments from @se-studio/core-ui.

Client Functions

  • createContentfulClient - Creates a Contentful Content Delivery API (CDA) client
  • createContentfulPreviewClient - Creates a Contentful Content Preview API (CPA) client
  • getContentfulClient - Gets the appropriate client based on preview mode

Converter Functions

  • createBaseConverterContext - Creates base converter context with default resolvers
  • basePageConverter - Base converter for pages
  • baseArticleConverter - Base converter for articles (full content)
  • baseArticleLinkConverter - Lightweight converter for article list/browse UIs. Returns IContentfulArticleLink. Resolves subtitle, visuals, tags, date, author, description, summary (rich text), and featuredImage without fetching full article content. Used internally by contentfulAllArticleLinks.
  • baseComponentConverter - Base converter for components
  • baseCollectionConverter - Base converter for collections

Revalidation Functions

  • revalidateTags - Revalidates multiple Next.js cache tags
  • revalidateSingleTag - Revalidates a single cache tag
  • createRevalidationHandler - Creates Next.js API route handler for webhook revalidation
  • getCacheTags - Gets cache tags based on content type and preview mode

Error Types

  • ContentfulError - Base error class for Contentful API errors
  • RateLimitError - Error for rate limit exceeded
  • EntryNotFoundError - Error for entry not found
  • AuthenticationError - Error for authentication failures
  • ValidationError - Error for validation failures

Utility Functions

  • isRetryableError - Check if an error is retryable
  • withRetry - Retry function with exponential backoff
  • RateLimiter - Rate limiter for controlling request rates

For detailed JSDoc documentation on all exports, see the TypeScript declaration files (.d.ts) in the package.

License

MIT

Repository

https://github.com/Something-Else-Studio/se-core-product