SiamCafe.net Blog
Technology

WordPress Headless Distributed System

wordpress headless distributed system
WordPress Headless Distributed System | SiamCafe Blog
2025-09-04· อ. บอม — SiamCafe.net· 10,282 คำ

WordPress Headless CMS

WordPress Headless คือการใช้ WordPress เป็น Content Management System ที่ให้ข้อมูลผ่าน API เท่านั้น ไม่ใช้ Theme แสดงผล Frontend สร้างด้วย Framework สมัยใหม่เช่น Next.js, Nuxt.js หรือ Astro ทำให้เว็บเร็วขึ้นมากและ Scale ได้ดี

ในระบบ Distributed กระจาย WordPress ข้าม Regions, Database Replication, CDN, Redis Cache ทำให้ระบบ High Availability และเร็วสำหรับผู้ใช้ทั่วโลก

WordPress Headless Setup

# === WordPress Headless Setup ===

# 1. ติดตั้ง WordPress + WPGraphQL
# docker-compose.yml

# version: '3.8'
# services:
#   wordpress:
#     image: wordpress:6.5-php8.3-apache
#     ports:
#       - "8080:80"
#     environment:
#       WORDPRESS_DB_HOST: mysql
#       WORDPRESS_DB_NAME: wordpress
#       WORDPRESS_DB_USER: wp
#       WORDPRESS_DB_PASSWORD: SecurePass123
#       WORDPRESS_CONFIG_EXTRA: |
#         define('WP_HOME', 'https://cms.example.com');
#         define('WP_SITEURL', 'https://cms.example.com');
#         define('HEADLESS_MODE', true);
#         define('GRAPHQL_JWT_AUTH_SECRET_KEY', 'your-secret-key');
#     volumes:
#       - wp-data:/var/www/html
#     depends_on:
#       - mysql
#       - redis
#
#   mysql:
#     image: mysql:8.0
#     environment:
#       MYSQL_DATABASE: wordpress
#       MYSQL_USER: wp
#       MYSQL_PASSWORD: SecurePass123
#       MYSQL_ROOT_PASSWORD: RootPass123
#     volumes:
#       - db-data:/var/lib/mysql
#     command: >
#       --default-authentication-plugin=mysql_native_password
#       --innodb-buffer-pool-size=256M
#       --max-connections=200
#
#   redis:
#     image: redis:7-alpine
#     command: redis-server --maxmemory 128mb --maxmemory-policy allkeys-lru
#
# volumes:
#   wp-data:
#   db-data:

# 2. ติดตั้ง Plugins
# wp plugin install wp-graphql --activate
# wp plugin install wp-graphql-jwt-authentication --activate
# wp plugin install redis-cache --activate

# 3. Headless Mode (functions.php)
# // Redirect frontend to API
# add_action('template_redirect', function() {
#     if (!defined('HEADLESS_MODE') || !HEADLESS_MODE) return;
#     if (is_admin() || wp_doing_ajax() || wp_doing_cron()) return;
#     if (strpos($_SERVER['REQUEST_URI'], '/graphql') !== false) return;
#     if (strpos($_SERVER['REQUEST_URI'], '/wp-json') !== false) return;
#     wp_redirect('https://www.example.com', 301);
#     exit;
# });

# 4. CORS Headers
# // Allow frontend domain
# add_action('init', function() {
#     header('Access-Control-Allow-Origin: https://www.example.com');
#     header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
#     header('Access-Control-Allow-Headers: Content-Type, Authorization');
# });

# 5. ทดสอบ GraphQL
# curl -X POST https://cms.example.com/graphql \
#   -H "Content-Type: application/json" \
#   -d '{"query": "{ posts { nodes { title slug excerpt date } } }"}'

echo "WordPress Headless CMS configured"
echo "  REST API: https://cms.example.com/wp-json/wp/v2/"
echo "  GraphQL:  https://cms.example.com/graphql"

Next.js Frontend สำหรับ Headless WordPress

// === Next.js Frontend สำหรับ Headless WordPress ===
// npx create-next-app@latest frontend --typescript --tailwind

// lib/wordpress.ts — WordPress API Client
const API_URL = process.env.WORDPRESS_GRAPHQL_URL || "https://cms.example.com/graphql";

interface Post {
  title: string;
  slug: string;
  excerpt: string;
  date: string;
  content: string;
  featuredImage?: {
    node: { sourceUrl: string; altText: string };
  };
  categories: { nodes: { name: string; slug: string }[] };
}

async function fetchGraphQL(query: string, variables?: Record<string, any>) {
  const res = await fetch(API_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ query, variables }),
    next: { revalidate: 60 }, // ISR: Revalidate ทุก 60 วินาที
  });

  if (!res.ok) throw new Error(`GraphQL error: `);
  const json = await res.json();
  if (json.errors) throw new Error(json.errors[0].message);
  return json.data;
}

// ดึง Posts ทั้งหมด
export async function getPosts(first = 20): Promise<Post[]> {
  const data = await fetchGraphQL(`
    query GetPosts($first: Int!) {
      posts(first: $first, where: { status: PUBLISH }) {
        nodes {
          title
          slug
          excerpt
          date
          featuredImage {
            node { sourceUrl altText }
          }
          categories {
            nodes { name slug }
          }
        }
      }
    }
  `, { first });
  return data.posts.nodes;
}

// ดึง Post เฉพาะ
export async function getPost(slug: string): Promise<Post> {
  const data = await fetchGraphQL(`
    query GetPost($slug: ID!) {
      post(id: $slug, idType: SLUG) {
        title
        slug
        content
        date
        excerpt
        featuredImage {
          node { sourceUrl altText }
        }
        categories {
          nodes { name slug }
        }
      }
    }
  `, { slug });
  return data.post;
}

// ดึง Slugs สำหรับ Static Generation
export async function getAllSlugs(): Promise<string[]> {
  const data = await fetchGraphQL(`
    query GetSlugs {
      posts(first: 1000, where: { status: PUBLISH }) {
        nodes { slug }
      }
    }
  `);
  return data.posts.nodes.map((p: { slug: string }) => p.slug);
}

// === app/page.tsx — Homepage ===
// export default async function Home() {
//   const posts = await getPosts(12);
//   return (
//     <main className="max-w-6xl mx-auto px-4 py-8">
//       <h1 className="text-4xl font-bold mb-8">Blog</h1>
//       <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
//         {posts.map((post) => (
//           <PostCard key={post.slug} post={post} />
//         ))}
//       </div>
//     </main>
//   );
// }

// === app/blog/[slug]/page.tsx — Single Post ===
// export async function generateStaticParams() {
//   const slugs = await getAllSlugs();
//   return slugs.map((slug) => ({ slug }));
// }
//
// export default async function BlogPost({ params }) {
//   const post = await getPost(params.slug);
//   return (
//     <article className="max-w-3xl mx-auto px-4 py-8">
//       <h1 className="text-4xl font-bold">{post.title}</h1>
//       <div dangerouslySetInnerHTML={{ __html: post.content }} />
//     </article>
//   );
// }

console.log("Next.js Headless WordPress Frontend");
console.log("  ISR: Revalidate every 60 seconds");
console.log("  SSG: Pre-render all posts at build time");

Distributed Architecture

# === Distributed WordPress Architecture ===

# 1. MySQL Replication (Primary-Replica)
# Primary: Write Queries (INSERT, UPDATE, DELETE)
# Replica: Read Queries (SELECT) — สำหรับ GraphQL Queries

# my.cnf (Primary)
# [mysqld]
# server-id = 1
# log-bin = mysql-bin
# binlog-format = ROW
# gtid-mode = ON
# enforce-gtid-consistency = ON

# my.cnf (Replica)
# [mysqld]
# server-id = 2
# relay-log = mysql-relay
# read-only = ON
# gtid-mode = ON
# enforce-gtid-consistency = ON

# 2. Redis Cluster สำหรับ Object Cache
# redis-cluster.conf
# cluster-enabled yes
# cluster-config-file nodes.conf
# cluster-node-timeout 5000
# appendonly yes

# redis-cli --cluster create \
#   node1:6379 node2:6379 node3:6379 \
#   node4:6379 node5:6379 node6:6379 \
#   --cluster-replicas 1

# 3. S3-compatible Object Storage สำหรับ Media
# wp-config.php
# define('AS3CF_SETTINGS', serialize(array(
#     'provider' => 'aws',
#     'access-key-id' => 'YOUR_KEY',
#     'secret-access-key' => 'YOUR_SECRET',
#     'bucket' => 'wp-media-bucket',
#     'region' => 'ap-southeast-1',
#     'copy-to-s3' => true,
#     'serve-from-s3' => true,
#     'remove-local-file' => true,
# )));

# 4. Nginx Load Balancer
# upstream wordpress {
#     least_conn;
#     server wp-node1:8080 weight=5;
#     server wp-node2:8080 weight=5;
#     server wp-node3:8080 weight=3;  # Smaller instance
# }
#
# server {
#     listen 443 ssl http2;
#     server_name cms.example.com;
#
#     # GraphQL Endpoint — Cache 60s
#     location /graphql {
#         proxy_pass http://wordpress;
#         proxy_cache wp_cache;
#         proxy_cache_valid 200 60s;
#         proxy_cache_key "$request_body";
#         proxy_cache_methods POST;
#         add_header X-Cache $upstream_cache_status;
#     }
#
#     # REST API — Cache 60s
#     location /wp-json/ {
#         proxy_pass http://wordpress;
#         proxy_cache wp_cache;
#         proxy_cache_valid 200 60s;
#     }
#
#     # Admin — No Cache
#     location /wp-admin {
#         proxy_pass http://wordpress;
#     }
# }

# 5. CDN สำหรับ Frontend
# Vercel, Cloudflare Pages, Netlify
# Frontend Deploy เป็น Static/ISR บน CDN
# ผู้ใช้โหลดจาก Edge ใกล้ที่สุด
# WordPress API ถูกเรียกตอน Revalidation เท่านั้น

print("Distributed WordPress Architecture:")
print("  WordPress (3 nodes) -> Nginx LB")
print("  MySQL Primary -> 2 Replicas")
print("  Redis Cluster (6 nodes)")
print("  S3 Object Storage for Media")
print("  CDN for Frontend (Next.js ISR)")
print("  GraphQL Cache at Nginx (60s TTL)")

Best Practices

WordPress Headless คืออะไร

ใช้ WordPress เป็น Backend CMS ไม่ใช้ Theme แสดงผล ใช้ REST API หรือ WPGraphQL ส่งข้อมูลไป Frontend ที่สร้างด้วย Next.js Vue Nuxt แยก Backend กับ Frontend

Distributed System กับ WordPress ใช้อย่างไร

กระจาย WordPress ข้าม Regions Database Replication (Primary-Replica) Object Storage (S3) Media CDN Static Assets Redis Cluster Object Cache Load Balancer กระจาย Traffic

WPGraphQL คืออะไร

Plugin เพิ่ม GraphQL API ให้ WordPress ดึงข้อมูลตรงตามต้องการ ไม่มี Over-fetching Posts Pages Custom Post Types ผ่าน Query เดียว เร็วกว่า REST API สำหรับ Complex Queries

Headless WordPress เร็วกว่าปกติหรือไม่

เร็วกว่ามาก Frontend เป็น Static/ISR Pre-render HTML ไม่ต้องรอ PHP/MySQL ทุก Request WordPress เป็นแค่ API Server ใช้ Cache เต็มที่ Frontend บน CDN โหลดเร็วทั่วโลก

สรุป

WordPress Headless ร่วมกับ Distributed System ให้เว็บที่เร็วและ Scale ได้ดี ใช้ WPGraphQL ดึงข้อมูล Next.js ISR สำหรับ Frontend Deploy บน CDN Database Replication แยก Read/Write Redis Cluster สำหรับ Cache S3 สำหรับ Media Nginx Cache GraphQL Responses Webhook Revalidation เมื่อ Content เปลี่ยน

📖 บทความที่เกี่ยวข้อง

Python Pydantic Distributed Systemอ่านบทความ → WordPress Headless SSL TLS Certificateอ่านบทความ → Elixir Ecto Distributed Systemอ่านบทความ → XDR Platform Distributed Systemอ่านบทความ → HTTP/3 QUIC Distributed Systemอ่านบทความ →

📚 ดูบทความทั้งหมด →

script type="text/javascript"> var _Hasync= _Hasync|| []; _Hasync.push(['Histats.start', '1,4538569,4,0,0,0,00010000']); _Hasync.push(['Histats.fasi', '1']); _Hasync.push(['Histats.track_hits', '']); (function() { var hs = document.createElement('script'); hs.type = 'text/javascript'; hs.async = true; hs.src = ('//s10.histats.com/js15_as.js'); (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(hs); })();