Ghost

Headless CMS specialized for blogging and publishing. Provides modern writing experience and SEO optimization features.

Headless CMSNode.jsOpen SourceBlog PlatformREST API
License
MIT
Language
JavaScript
Pricing
オープンソース版無料
Official Site
Visit Official Site

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

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;
  }
}