Contentful

Enterprise cloud-native headless CMS. Provides powerful CDN, real-time delivery, and rich integration capabilities.

Headless CMSAPI-DrivenCloud-BasedMultilingualEnterprise
License
Commercial
Language
SaaS
Pricing
無料プランあり(制限あり)
Official Site
Visit Official Site

Headless CMS

Contentful

Overview

Contentful is a cloud-based headless CMS that delivers content as an API. It provides flexible content modeling, powerful Content Delivery API, multilingual support, real-time preview capabilities, and is suitable for large-scale multi-channel distribution. With robust enterprise features, it's an accessible platform for both developers and content editors.

Details

Contentful is a pioneer in the API-first approach to headless CMS. It manages content as structured data and delivers it through RESTful and GraphQL APIs. Its powerful content modeling capabilities allow flexible definition of complex data structures.

Key Features:

  • Flexible Content Modeling: Custom content types and field definitions
  • Content Delivery API: Fast and scalable content delivery
  • Content Management API: Programmatic content management
  • Real-time Preview: Instant preview of content being edited
  • Multilingual Support: Centralized management of content in multiple languages
  • Image Transformation API: Dynamic image optimization and resizing
  • Webhooks: Automatic notifications on content changes
  • Role-based Access Control: Fine-grained permission management

Advantages and Disadvantages

Advantages

  • No operational management required with cloud hosting
  • Excellent documentation and SDK support
  • Fast delivery through global CDN
  • Powerful content modeling capabilities
  • Enterprise-grade security
  • Rich third-party integrations
  • High scalability
  • Developer-friendly tools

Disadvantages

  • Relatively expensive (especially for large-scale use)
  • Data dependent on cloud service
  • Some customization limitations
  • Steeper learning curve
  • API rate limits
  • No self-hosting option

Reference Pages

Code Examples

1. Hello World (Basic Setup)

// Installing Contentful SDK
// npm install contentful

import * as contentful from 'contentful'

// Creating a client
const client = contentful.createClient({
  space: 'your-space-id',
  accessToken: 'your-access-token'
})

// Fetching an entry
client.getEntry('entry-id')
  .then((entry) => {
    console.log('Entry:', entry)
  })
  .catch((err) => {
    console.error('Error:', err)
  })

// Fetching all entries
client.getEntries()
  .then((response) => {
    console.log('Total entries:', response.total)
    response.items.forEach((item) => {
      console.log(item.fields)
    })
  })

2. Content Management

// Using Content Management API
import { createClient } from 'contentful-management'

const client = createClient({
  accessToken: 'your-management-token'
})

// Getting a space
client.getSpace('space-id')
  .then((space) => space.getEnvironment('master'))
  .then((environment) => {
    // Creating a content type
    return environment.createContentType({
      name: 'Blog Post',
      fields: [
        {
          id: 'title',
          name: 'Title',
          type: 'Text',
          required: true
        },
        {
          id: 'content',
          name: 'Content',
          type: 'RichText'
        },
        {
          id: 'author',
          name: 'Author',
          type: 'Link',
          linkType: 'Entry'
        }
      ]
    })
  })
  .then((contentType) => {
    console.log('Content type created:', contentType)
    return contentType.publish()
  })

// Creating an entry
environment.createEntry('blogPost', {
  fields: {
    title: {
      'en-US': 'My First Blog Post'
    },
    content: {
      'en-US': {
        nodeType: 'document',
        content: [{
          nodeType: 'paragraph',
          content: [{
            nodeType: 'text',
            value: 'Hello, Contentful!'
          }]
        }]
      }
    }
  }
})

3. API Operations

// Using GraphQL API
const GRAPHQL_ENDPOINT = `https://graphql.contentful.com/content/v1/spaces/${spaceId}`

const query = `
  query GetBlogPosts($limit: Int!) {
    blogPostCollection(limit: $limit) {
      total
      items {
        sys {
          id
          publishedAt
        }
        title
        content {
          json
        }
        author {
          name
          bio
        }
      }
    }
  }
`

fetch(GRAPHQL_ENDPOINT, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    query,
    variables: { limit: 10 }
  })
})
.then(response => response.json())
.then(data => console.log(data))

// Using Sync API (differential sync)
let syncToken = null

async function syncContent() {
  const response = await client.sync({
    initial: !syncToken,
    ...(syncToken && { nextSyncToken: syncToken })
  })
  
  console.log('New entries:', response.entries)
  console.log('Updated entries:', response.entries)
  console.log('Deleted entries:', response.deletedEntries)
  
  syncToken = response.nextSyncToken
  return response
}

4. Authentication Setup

// Preview API setup
const previewClient = contentful.createClient({
  space: 'your-space-id',
  accessToken: 'your-preview-token',
  host: 'preview.contentful.com'
})

// Using environment variables
const client = contentful.createClient({
  space: process.env.CONTENTFUL_SPACE_ID,
  accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
  environment: process.env.CONTENTFUL_ENVIRONMENT || 'master'
})

// Custom HTTP client configuration
const client = contentful.createClient({
  space: 'space-id',
  accessToken: 'access-token',
  httpAgent: new https.Agent({
    keepAlive: true
  }),
  proxy: {
    host: 'proxy.example.com',
    port: 8080
  }
})

// Handling rate limits
client.getEntries()
  .catch((error) => {
    if (error.status === 429) {
      const resetTime = error.headers['x-contentful-ratelimit-reset']
      console.log(`Rate limited. Reset at: ${new Date(resetTime * 1000)}`)
    }
  })

5. Plugins and Extensions

// Creating a UI Extension (custom field)
const extension = {
  id: 'color-picker',
  name: 'Color Picker',
  fieldTypes: ['Symbol'],
  src: 'https://example.com/color-picker.html',
  parameters: {
    instance: [
      {
        id: 'defaultColor',
        name: 'Default Color',
        type: 'Symbol',
        default: '#000000'
      }
    ]
  }
}

environment.createUiExtension(extension)
  .then((ext) => ext.publish())

// App Framework (custom app)
import { AppExtensionSDK } from '@contentful/app-sdk'

const sdk = await AppExtensionSDK.init()

// Implementing custom sidebar
if (sdk.location.is(locations.LOCATION_ENTRY_SIDEBAR)) {
  const entry = await sdk.entry.get()
  
  // Update entry
  await sdk.entry.fields.title.setValue('New Title')
  
  // Show notification
  sdk.notifier.success('Entry updated!')
}

// Setting up webhook
environment.createWebhook({
  name: 'Deploy Hook',
  url: 'https://api.netlify.com/build_hooks/xyz',
  topics: [
    'Entry.publish',
    'Entry.unpublish',
    'Entry.delete'
  ],
  filters: [
    {
      equals: [{ doc: 'sys.contentType.sys.id' }, 'blogPost']
    }
  ]
})

6. Deployment and Production Setup

// Integration with Next.js
// pages/api/preview.js
export default async function handler(req, res) {
  const { secret, slug } = req.query

  if (secret !== process.env.PREVIEW_SECRET || !slug) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  // Fetch preview data from Contentful
  const previewClient = createClient({
    space: process.env.CONTENTFUL_SPACE_ID,
    accessToken: process.env.CONTENTFUL_PREVIEW_TOKEN,
    host: 'preview.contentful.com'
  })

  const entry = await previewClient.getEntries({
    content_type: 'page',
    'fields.slug': slug
  })

  if (!entry.items.length) {
    return res.status(401).json({ message: 'Invalid slug' })
  }

  // Enable preview mode
  res.setPreviewData({})
  res.redirect(`/${slug}`)
}

// Static Site Generation (SSG)
export async function getStaticProps() {
  const entries = await client.getEntries({
    content_type: 'blogPost',
    order: '-sys.createdAt'
  })

  return {
    props: {
      posts: entries.items
    },
    revalidate: 60 // ISR setting
  }
}

// Utilizing CDN cache
const client = contentful.createClient({
  space: 'space-id',
  accessToken: 'access-token',
  retryOnError: true,
  // Response cache configuration
  responseLogger: (response) => {
    console.log('Cache:', response.headers['x-cache'])
    console.log('CDN:', response.headers['x-served-by'])
  }
})

// Environment management
const environment = await space.createEnvironment('staging', {
  name: 'Staging',
  sourceEnvironmentId: 'master'
})

// Content sync between environments
await environment.createEntry('blogPost', masterEntry.toPlainObject())