Ghost
Headless CMS specialized for blogging and publishing. Provides modern writing experience and SEO optimization features.
Headless CMS
Ghost
Overview
Ghost is an open-source headless CMS built with Node.js. Originally developed as a blogging platform, it functions as a complete headless CMS by providing powerful Content API and Admin API. It combines a beautiful Ember.js admin interface with a RESTful JSON API for developers, enabling efficient content creation, management, and delivery.
Details
Ghost adopts an API-first architecture, providing two main APIs: Content API (read-only) and Admin API (read/write). In addition to standard blogging features, it includes custom pages, tags, author management, SEO optimization, and excels at integration with static site generators.
Key Features:
- Content API: Read-only access to published content
- Admin API: Full read/write access with role-based authentication
- Ember.js Admin Interface: Beautiful and user-friendly editor
- Markdown Support: Markdown editor and rich text editor
- Built-in SEO: Automatic metadata generation and SEO optimization
- Image Optimization: Automatic image resizing and optimization
- Membership Features: Paid subscriptions and member management (limited in headless mode)
- Theme System: Handlebars template engine
Advantages and Disadvantages
Advantages
- Completely open source (MIT License)
- Excellent compatibility with Node.js ecosystem
- Superior editor experience
- Fast performance
- Comprehensive API documentation
- Managed service through Ghost(Pro)
- Active community
- Rich SEO features
Disadvantages
- Membership features unavailable in headless mode
- Not suitable for complex content modeling
- Limited plugin system
- Few enterprise features
- Additional work required for frontend integration
- Low webhook flexibility
Reference Pages
- Official Website
- Official Documentation
- GitHub Repository
- Content API Documentation
- Admin API Documentation
- JAMstack Guide
Code Examples
1. Hello World (Basic Setup)
# Install Ghost (development environment)
npm install ghost-cli@latest -g
# Set up Ghost in a new directory
mkdir my-ghost-blog && cd my-ghost-blog
ghost install local
# Production installation
ghost install
// Configure Content API client
import GhostContentAPI from '@tryghost/content-api'
const api = new GhostContentAPI({
url: 'https://your-ghost-site.com',
key: 'your-content-api-key',
version: 'v5.0'
})
// Fetch latest posts
api.posts
.browse({ limit: 5, include: 'tags,authors' })
.then((posts) => {
posts.forEach((post) => {
console.log(post.title)
})
})
.catch((err) => {
console.error(err)
})
2. Content Management
// Content creation using Admin API
import GhostAdminAPI from '@tryghost/admin-api'
const adminApi = new GhostAdminAPI({
url: 'https://your-ghost-site.com',
key: 'your-admin-api-key',
version: 'v5.0'
})
// Create a new post
const newPost = {
title: 'My New Post',
slug: 'my-new-post',
mobiledoc: JSON.stringify({
version: '0.3.1',
atoms: [],
cards: [],
markups: [],
sections: [
[1, 'p', [[0, [], 0, 'This is my post content']]]
]
}),
status: 'published',
tags: ['Getting Started'],
authors: ['your-author-id']
}
adminApi.posts.add(newPost)
.then(response => console.log(response))
.catch(error => console.error(error))
// Update a post
adminApi.posts.edit({
id: 'post-id',
updated_at: 'current-updated-at',
title: 'Updated Title'
})
.then(response => console.log(response))
.catch(error => console.error(error))
3. API Operations
// Advanced queries and filtering
// Filter by tag
api.posts
.browse({
filter: 'tag:javascript',
limit: 10,
include: 'tags,authors'
})
.then(posts => console.log(posts))
// Pagination
api.posts
.browse({
page: 2,
limit: 15
})
.then(posts => {
console.log(posts.meta.pagination)
})
// Fetch single post
api.posts
.read({ slug: 'welcome' })
.then(post => console.log(post))
// Get tags
api.tags
.browse({ limit: 'all' })
.then(tags => {
tags.forEach(tag => {
console.log(`${tag.name}: ${tag.count.posts} posts`)
})
})
// Get author information
api.authors
.browse({ include: 'count.posts' })
.then(authors => console.log(authors))
4. Authentication Setup
// Getting and setting Content API key
// Ghost Admin → Integrations → Custom Integration → Add
// Environment variable management
const api = new GhostContentAPI({
url: process.env.GHOST_API_URL,
key: process.env.GHOST_CONTENT_API_KEY,
version: 'v5.0'
})
// Admin API authentication (JWT)
import jwt from 'jsonwebtoken'
function getAdminToken() {
const [id, secret] = process.env.GHOST_ADMIN_API_KEY.split(':')
const token = jwt.sign({}, Buffer.from(secret, 'hex'), {
keyid: id,
algorithm: 'HS256',
expiresIn: '5m',
audience: `/admin/`
})
return token
}
// Authentication with custom headers
fetch(`${ghostUrl}/ghost/api/admin/posts/`, {
headers: {
'Authorization': `Ghost ${getAdminToken()}`,
'Content-Type': 'application/json'
}
})
5. Plugins and Extensions
// Webhook configuration (set from Ghost admin)
// Settings → Integrations → Webhooks
// Example webhook receiving endpoint
app.post('/webhook/ghost', (req, res) => {
const { post } = req.body
// Process when post is published
if (post.current.status === 'published') {
console.log(`New post published: ${post.current.title}`)
// Trigger static site rebuild
triggerStaticSiteBuild()
}
res.status(200).send('OK')
})
// Create custom helper
// themes/your-theme/helpers/custom-helper.js
module.exports = function customHelper(options) {
// Custom logic
return new Handlebars.SafeString(output)
}
// Custom app extending the API
const customApp = express()
customApp.get('/api/custom/stats', async (req, res) => {
const posts = await api.posts.browse({ limit: 'all' })
const stats = {
totalPosts: posts.length,
totalWords: posts.reduce((sum, post) => sum + post.word_count, 0)
}
res.json(stats)
})
6. Deployment and Production Setup
// Deployment to Ghost(Pro) is automatic
// For self-hosting
// config.production.json
{
"url": "https://your-domain.com",
"server": {
"port": 2368,
"host": "0.0.0.0"
},
"database": {
"client": "mysql",
"connection": {
"host": "localhost",
"user": "ghost",
"password": "your-password",
"database": "ghost_production"
}
},
"mail": {
"transport": "SMTP",
"options": {
"service": "Mailgun",
"auth": {
"user": "[email protected]",
"pass": "your-mailgun-password"
}
}
}
}
// Next.js integration example
// pages/index.js
export async function getStaticProps() {
const posts = await api.posts.browse({
limit: 'all',
include: 'tags,authors',
filter: 'status:published'
})
return {
props: { posts },
revalidate: 60 // ISR
}
}
// Gatsby integration
// gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-source-ghost`,
options: {
apiUrl: `https://your-ghost-site.com`,
contentApiKey: `your-content-api-key`,
}
}
]
}
// Nginx reverse proxy configuration
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:2368;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}