Use Cases

Implementasi Contentful CMS dengan Claude Code

Pelajari tentang implementasi Contentful CMS menggunakan Claude Code. Dilengkapi tips praktis dan contoh kode.

Efisiensi Integrasi Contentful CMS dengan Claude Code

Contentful adalah salah satu headless CMS terkemuka yang mewujudkan manajemen konten berbasis API-first. Dengan model konten terstruktur dan API delivery yang kaya, konten bisa diambil dari frontend mana pun. Dengan Claude Code, mulai dari desain model konten hingga integrasi frontend bisa dilakukan secara efisien.

Desain Model Konten

> Desain model konten untuk situs blog di Contentful.
> Dengan 3 content type: artikel, kategori, dan penulis.
// scripts/setup-content-model.ts
import contentful from 'contentful-management';

const client = contentful.createClient({
  accessToken: process.env.CONTENTFUL_MANAGEMENT_TOKEN!,
});

async function setupContentModel() {
  const space = await client.getSpace(process.env.CONTENTFUL_SPACE_ID!);
  const env = await space.getEnvironment('master');

  // Content type penulis
  const author = await env.createContentTypeWithId('author', {
    name: 'Author',
    fields: [
      { id: 'name', name: 'Name', type: 'Symbol', required: true },
      { id: 'slug', name: 'Slug', type: 'Symbol', required: true },
      { id: 'bio', name: 'Bio', type: 'Text' },
      { id: 'avatar', name: 'Avatar', type: 'Link', linkType: 'Asset' },
    ],
  });
  await author.publish();

  // Kategori
  const category = await env.createContentTypeWithId('category', {
    name: 'Category',
    fields: [
      { id: 'name', name: 'Name', type: 'Symbol', required: true },
      { id: 'slug', name: 'Slug', type: 'Symbol', required: true },
      { id: 'description', name: 'Description', type: 'Text' },
    ],
  });
  await category.publish();

  // Artikel blog
  const blogPost = await env.createContentTypeWithId('blogPost', {
    name: 'Blog Post',
    fields: [
      { id: 'title', name: 'Title', type: 'Symbol', required: true },
      { id: 'slug', name: 'Slug', type: 'Symbol', required: true },
      { id: 'excerpt', name: 'Excerpt', type: 'Text' },
      { id: 'body', name: 'Body', type: 'RichText' },
      { id: 'featuredImage', name: 'Featured Image', type: 'Link', linkType: 'Asset' },
      { id: 'author', name: 'Author', type: 'Link', linkType: 'Entry' },
      { id: 'category', name: 'Category', type: 'Link', linkType: 'Entry' },
      { id: 'tags', name: 'Tags', type: 'Array', items: { type: 'Symbol' } },
      { id: 'publishedAt', name: 'Published At', type: 'Date' },
    ],
  });
  await blogPost.publish();
}

Mengambil Konten

> Buatkan client untuk mengambil artikel blog dengan
> Content Delivery API Contentful. Generate juga tipe TypeScript-nya.
// src/lib/contentful.ts
import { createClient, type Entry, type Asset } from 'contentful';

const client = createClient({
  space: process.env.CONTENTFUL_SPACE_ID!,
  accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!,
});

// Client untuk preview
const previewClient = createClient({
  space: process.env.CONTENTFUL_SPACE_ID!,
  accessToken: process.env.CONTENTFUL_PREVIEW_TOKEN!,
  host: 'preview.contentful.com',
});

function getClient(preview = false) {
  return preview ? previewClient : client;
}

// Definisi tipe
interface BlogPostFields {
  title: string;
  slug: string;
  excerpt: string;
  body: any; // Rich Text Document
  featuredImage: Asset;
  author: Entry<AuthorFields>;
  category: Entry<CategoryFields>;
  tags: string[];
  publishedAt: string;
}

interface AuthorFields {
  name: string;
  slug: string;
  bio: string;
  avatar: Asset;
}

interface CategoryFields {
  name: string;
  slug: string;
  description: string;
}

// Mengambil daftar artikel
export async function getBlogPosts(options?: {
  limit?: number;
  skip?: number;
  category?: string;
  preview?: boolean;
}) {
  const { limit = 10, skip = 0, category, preview = false } = options || {};

  const query: any = {
    content_type: 'blogPost',
    order: ['-fields.publishedAt'],
    limit,
    skip,
    include: 2,
  };

  if (category) {
    query['fields.category.sys.contentType.sys.id'] = 'category';
    query['fields.category.fields.slug'] = category;
  }

  const entries = await getClient(preview).getEntries<BlogPostFields>(query);

  return {
    posts: entries.items,
    total: entries.total,
  };
}

// Mengambil artikel individual
export async function getBlogPost(slug: string, preview = false) {
  const entries = await getClient(preview).getEntries<BlogPostFields>({
    content_type: 'blogPost',
    'fields.slug': slug,
    include: 2,
    limit: 1,
  });

  return entries.items[0] || null;
}

Implementasi ISR di Next.js

// app/blog/[slug]/page.tsx
import { getBlogPost, getBlogPosts } from '@/lib/contentful';
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { notFound } from 'next/navigation';

export const revalidate = 60; // Revalidasi setiap 60 detik

export async function generateStaticParams() {
  const { posts } = await getBlogPosts({ limit: 100 });
  return posts.map((post) => ({ slug: post.fields.slug }));
}

export default async function BlogPostPage({
  params,
}: {
  params: { slug: string };
}) {
  const post = await getBlogPost(params.slug);
  if (!post) notFound();

  return (
    <article className="max-w-3xl mx-auto p-6">
      <h1 className="text-4xl font-bold">{post.fields.title}</h1>
      <p className="text-gray-500 mt-2">
        {new Date(post.fields.publishedAt).toLocaleDateString('en-US')}
      </p>
      <div className="prose mt-8">
        {documentToReactComponents(post.fields.body)}
      </div>
    </article>
  );
}

Summary

Dengan menggabungkan pendekatan API-first Contentful dan Claude Code, kamu bisa membangun sistem manajemen konten yang fleksibel secara efisien. Panduan Membangun Blog CMS dan Perbandingan SSR/SSG juga bisa dijadikan referensi.

Untuk detail Contentful, lihat Dokumentasi Resmi Contentful.

#Claude Code #Contentful #CMS #Headless CMS #Content Management