Hugo dengan Headless CMS: Integrasi Strapi, Contentful, dan Sanity
Hugo dengan Headless CMS: Integrasi Strapi, Contentful, dan Sanity
Headless CMS adalah pendekatan modern dalam content management yang memisahkan backend content repository dari frontend presentation layer. Hugo sebagai static site generator dapat diintegrasikan dengan berbagai headless CMS untuk memberikan pengalaman editing yang powerful kepada content creators. Panduan ini akan membahas cara mengintegrasikan Hugo dengan tiga headless CMS populer: Strapi, Contentful, dan Sanity.
Integrasi headless CMS dengan Hugo memberikan yang terbaik dari dua dunia: performa dan keamanan static site dari Hugo, dengan flexible dan user-friendly content management dari headless CMS. Pendekatan ini sangat cocok untuk tim yang menginginkan content management capabilities tanpa mengorbankan static site benefits.
Keunggulan Headless CMS dengan Hugo
Integrasi headless CMS dengan Hugo menawarkan banyak keunggulan yang menjadikannya pilihan populer untuk proyek modern. Content creators dapat menggunakan interface yang familiar untuk mengelola konten tanpa perlu memahami Git atau Markdown. API-driven content memungkinkan konten dari berbagai sources untuk di-aggregate dan di-present secara konsisten. Multi-channel publishing memungkinkan konten yang sama di-deploy ke website, mobile apps, dan platform lain. Scalability yang excellent karena headless CMS dapat handle konten dalam jumlah besar dengan baik.
Integrasi dengan Strapi
Strapi adalah open-source headless CMS yang dibangun dengan Node.js. Strapi menawarkan flexibility dan customization yang tinggi.
Setup Strapi
# Install Strapi
npx create-strapi-app@latest my-strapi --quickstart
Content Types di Strapi
Buat content types sesuai kebutuhan:
Article Collection Type:
- title (Text)
- slug (UID)
- content (Rich Text)
- excerpt (Text)
- featuredImage (Media)
- category (Relation to Category)
- publishedAt (Datetime)
Category Collection Type:
- name (Text)
- slug (UID)
- description (Text)
Fetch Content dari Strapi di Hugo
Buat script untuk fetch dan generate konten:
// scripts/fetch-strapi.js
const fs = require('fs');
const path = require('path');
const fetch = require('node-fetch');
const STRAPI_URL = process.env.STRAPI_URL || 'http://localhost:1337';
const API_TOKEN = process.env.STRAPI_API_TOKEN;
async function fetchContent() {
// Fetch articles
const articlesRes = await fetch(`${STRAPI_URL}/api/articles?populate=*`, {
headers: {
'Authorization': `Bearer ${API_TOKEN}`
}
});
const articles = await articlesRes.json();
// Generate markdown files
articles.data.forEach(article => {
const frontmatter = {
title: article.attributes.title,
slug: article.attributes.slug,
date: article.attributes.publishedAt,
description: article.attributes.excerpt,
image: article.attributes.featuredImage?.data?.attributes?.url,
categories: [article.attributes.category?.data?.attributes?.name]
};
const markdown = `---\n${yaml.stringify(frontmatter)}---\n\n${article.attributes.content}`;
const filePath = path.join(__dirname, '../content/articles', `${article.attributes.slug}.md`);
fs.writeFileSync(filePath, markdown);
});
console.log(`Generated ${articles.data.length} articles`);
}
fetchContent().catch(console.error);
Hubungkan dengan Build Pipeline
// package.json
{
"scripts": {
"prebuild": "node scripts/fetch-strapi.js",
"build": "hugo",
"dev": "node scripts/fetch-strapi.js && hugo server"
}
}
Integrasi dengan Contentful
Contentful adalah cloud-based headless CMS yang menawarkan reliability dan features enterprise-grade.
Setup Contentful
- Buat account di contentful.com
- Create space baru
- Buat content types
- Generate API credentials
Content Model di Contentful
Blog Post Content Type:
- title (Short text)
- slug (Short text, generate from title)
- body (Long text atau Rich text)
- featuredImage (Media)
- publishDate (Date & time)
- author (Reference ke Author content type)
Fetch Content dari Contentful
// scripts/fetch-contentful.js
const fs = require('fs');
const path = require('path');
const contentful = require('contentful');
const client = contentful.createClient({
space: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});
async function fetchPosts() {
const entries = await client.getEntries({
content_type: 'blogPost',
order: '-fields.publishDate'
});
entries.items.forEach(entry => {
const frontmatter = {
title: entry.fields.title,
slug: entry.fields.slug,
date: entry.fields.publishDate,
description: entry.fields.excerpt,
image: entry.fields.featuredImage?.fields?.file?.url,
author: entry.fields.author?.fields?.name
};
const markdown = `---\n${yaml.stringify(frontmatter)}---\n\n${entry.fields.body}`;
const filePath = path.join(__dirname, '../content/posts', `${entry.fields.slug}.md`);
fs.writeFileSync(filePath, markdown);
});
console.log(`Generated ${entries.items.length} posts`);
}
fetchPosts().catch(console.error);
Setup Environment Variables
# .env
CONTENTFUL_SPACE_ID=your_space_id
CONTENTFUL_ACCESS_TOKEN=your_access_token
Integrasi dengan Sanity
Sanity adalah headless CMS dengan real-time collaboration dan structured content capabilities.
Setup Sanity
# Install Sanity CLI
npm install -g @sanity/cli
# Initialize project
sanity init
Schema di Sanity
// sanity/schemas/post.js
export default {
name: 'post',
title: 'Post',
type: 'document',
fields: [
{
name: 'title',
title: 'Title',
type: 'string',
},
{
name: 'slug',
title: 'Slug',
type: 'slug',
options: {
source: 'title',
},
},
{
name: 'body',
title: 'Body',
type: 'array',
of: [{type: 'block'}],
},
{
name: 'featuredImage',
title: 'Featured Image',
type: 'image',
options: {
hotspot: true,
},
},
{
name: 'categories',
title: 'Categories',
type: 'array',
of: [{type: 'reference', to: {type: 'category'}}],
},
],
}
Fetch Content dari Sanity
// scripts/fetch-sanity.js
const fs = require('fs');
const path = require('path');
const { createClient } = require('@sanity/client');
const client = createClient({
projectId: process.env.SANITY_PROJECT_ID,
dataset: 'production',
useCdn: true,
apiVersion: '2024-01-01',
});
async function fetchPosts() {
const query = `*[_type == "post"] | order(_createdAt desc) {
_id,
title,
"slug": slug.current,
_createdAt,
body,
"imageUrl": featuredImage.asset->url
}`;
const posts = await client.fetch(query);
posts.forEach(post => {
const frontmatter = {
title: post.title,
slug: post.slug,
date: post._createdAt,
description: 'Post description',
image: post.imageUrl
};
// Convert Portable Text ke Markdown
const body = convertPortableTextToMarkdown(post.body);
const markdown = `---\n${yaml.stringify(frontmatter)}---\n\n${body}`;
const filePath = path.join(__dirname, '../content/posts', `${post.slug}.md`);
fs.writeFileSync(filePath, markdown);
});
console.log(`Generated ${posts.length} posts`);
}
fetchPosts().catch(console.error);
Automate dengan Webhooks
Setup Webhook di CMS
Contentful Webhook:
- URL:
https://your-cicd.com/webhook/contentful - Trigger: Publish, Unpublish
Strapi Webhook:
- URL:
https://your-cicd.com/webhook/strapi - Events: After Publish, After Update
Sanity Webhook:
- URL:
https://your-cicd.com/webhook/sanity - Trigger: On Create, On Update, On Delete
CI/CD Webhook Handler
// functions/webhook.js
const execSync = require('child_process').execSync;
exports.handler = async (event) => {
// Verify webhook signature
// const signature = event.headers['x-webhook-signature'];
// Trigger rebuild
try {
execSync('git pull origin main', { cwd: '/path/to/repo' });
execSync('npm run build', { cwd: '/path/to/repo' });
return { statusCode: 200, body: 'Build triggered' };
} catch (error) {
return { statusCode: 500, body: error.message };
}
};
Perbandingan Headless CMS
| Fitur | Strapi | Contentful | Sanity |
|---|---|---|---|
| Pricing | Free (self-hosted) | Free tier + paid | Free tier + paid |
| Self-hosted | Ya | Tidak | Tidak |
| Real-time collab | Tidak | Ya | Ya |
| API | GraphQL/REST | GraphQL/REST | GROQ |
| Image CDN | Custom | Built-in | Built-in |
| Portable Text | Plugin | Rich Text | Native |
| Webhooks | Ya | Ya | Ya |
Best Practices
Caching Strategy
Implementasikan caching untuk mengurangi API calls:
// scripts/cache-content.js
const cache = new Map();
const CACHE_TTL = 3600000; // 1 hour
async function getContent(key, fetcher) {
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}
const data = await fetcher();
cache.set(key, { data, timestamp: Date.now() });
return data;
}
Content Sync
Implementasikan incremental sync untuk performance:
// scripts/incremental-sync.js
async function syncContent() {
// Get last sync timestamp
const lastSync = getLastSyncTimestamp() || 0;
// Fetch only updated content
const updates = await fetchUpdatesSince(lastSync);
// Process updates
for (const item of updates) {
await updateContentFile(item);
}
// Update sync timestamp
updateLastSyncTimestamp(Date.now());
}
Image Handling
Lakukan download dan optimize images lokal:
// scripts/download-images.js
const download = require('download');
const sharp = require('sharp');
async function processImages() {
const posts = getAllPosts();
for (const post of posts) {
if (post.image) {
const imageBuffer = await download(post.image);
await sharp(imageBuffer)
.resize(800, null, { withoutEnlargement: true })
.webp({ quality: 80 })
.toFile(`static/img/${post.slug}.webp`);
}
}
}
Kesimpulan
Integrasi Hugo dengan headless CMS memberikan fleksibilitas dalam content management sambil mempertahankan static site benefits. Pilih headless CMS berdasarkan kebutuhan tim, budget, dan technical requirements. Strapi untuk kontrol penuh dengan self-hosted, Contentful untuk enterprise-grade reliability, atau Sanity untuk real-time collaboration.
Artikel Terkait
Link Postingan: https://www.tirinfo.com/hugo-headless-cms/