WP Headless
WordPress Headless Citizen Developer WP REST API WPGraphQL Next.js Nuxt.js Astro Content Modeling ACF Custom Post Type Deployment CDN Production
| Approach | Frontend | Data Fetching | Performance | Complexity | เหมาะกับ |
|---|---|---|---|---|---|
| Traditional WP | PHP Theme | Direct DB | ปานกลาง | ง่าย | Simple blog |
| Headless + Next.js | React SSR/SSG | REST/GraphQL | ดีมาก | ปานกลาง | Dynamic site |
| Headless + Astro | Static HTML | REST at build | ดีมากที่สุด | ง่าย | Content site |
| Headless + Nuxt | Vue SSR/SSG | REST/GraphQL | ดีมาก | ปานกลาง | Vue team |
| Headless + Gatsby | React Static | GraphQL | ดีมาก | สูง | Large static |
WP REST API
# === WordPress REST API Usage ===
# Endpoints
# GET /wp-json/wp/v2/posts — List posts
# GET /wp-json/wp/v2/posts/123 — Single post
# GET /wp-json/wp/v2/pages — List pages
# GET /wp-json/wp/v2/categories — List categories
# GET /wp-json/wp/v2/tags — List tags
# GET /wp-json/wp/v2/media — List media
# GET /wp-json/wp/v2/users — List users
# Query Parameters
# ?per_page=10&page=1 — Pagination
# ?search=keyword — Search
# ?categories=5 — Filter by category
# ?orderby=date&order=desc — Sort
# ?_embed — Include related data
# ?slug=my-post-slug — Find by slug
# ?status=publish — Published only
# Next.js Data Fetching
# // pages/blog/[slug].js
# export async function getStaticPaths() {
# const res = await fetch('https://wp.example.com/wp-json/wp/v2/posts?per_page=100');
# const posts = await res.json();
# return {
# paths: posts.map(post => ({ params: { slug: post.slug } })),
# fallback: 'blocking',
# };
# }
#
# export async function getStaticProps({ params }) {
# const res = await fetch(
# `https://wp.example.com/wp-json/wp/v2/posts?slug=&_embed`
# );
# const [post] = await res.json();
# return { props: { post }, revalidate: 60 };
# }
from dataclasses import dataclass
@dataclass
class APIEndpoint:
method: str
path: str
auth: bool
description: str
example: str
endpoints = [
APIEndpoint("GET", "/wp/v2/posts", False, "List all posts", "?per_page=10&_embed"),
APIEndpoint("GET", "/wp/v2/posts?slug=xxx", False, "Get post by slug", "?slug=my-post&_embed"),
APIEndpoint("POST", "/wp/v2/posts", True, "Create new post", '{"title":"New","content":"...","status":"publish"}'),
APIEndpoint("PUT", "/wp/v2/posts/123", True, "Update post", '{"title":"Updated Title"}'),
APIEndpoint("DELETE", "/wp/v2/posts/123", True, "Delete post", "?force=true"),
APIEndpoint("GET", "/wp/v2/media", False, "List media files", "?per_page=20&media_type=image"),
]
print("=== WP REST API Endpoints ===")
for e in endpoints:
auth = "Auth Required" if e.auth else "Public"
print(f" [{e.method}] {e.path} ({auth})")
print(f" {e.description}")
print(f" Example: {e.example}")
Content Modeling
# === Content Modeling with ACF ===
# ACF (Advanced Custom Fields) Plugin
# Create Custom Post Type: "Products"
# functions.php:
# register_post_type('product', [
# 'label' => 'Products',
# 'public' => true,
# 'show_in_rest' => true, // Enable REST API
# 'supports' => ['title', 'editor', 'thumbnail', 'custom-fields'],
# ]);
# ACF Field Groups:
# Product Details:
# - price (Number)
# - sku (Text)
# - stock (Number)
# - gallery (Gallery)
# - specifications (Repeater)
# - spec_name (Text)
# - spec_value (Text)
# REST API with ACF:
# GET /wp-json/wp/v2/product?_embed
# Response includes acf: { price: 999, sku: "SKU-001", ... }
# WPGraphQL + ACF:
# query {
# products(first: 10) {
# nodes {
# title
# productDetails {
# price
# sku
# stock
# }
# }
# }
# }
@dataclass
class ContentModel:
post_type: str
fields: str
rest_endpoint: str
use_case: str
models = [
ContentModel("Posts", "title content excerpt categories tags featured_image", "/wp/v2/posts", "Blog articles"),
ContentModel("Products", "price sku stock gallery specifications", "/wp/v2/product", "E-commerce catalog"),
ContentModel("Events", "date location speaker capacity registration_url", "/wp/v2/event", "Event listings"),
ContentModel("Testimonials", "author_name company rating quote photo", "/wp/v2/testimonial", "Social proof"),
ContentModel("Portfolio", "client project_url technologies gallery", "/wp/v2/portfolio", "Work showcase"),
ContentModel("FAQ", "question answer category order", "/wp/v2/faq", "FAQ sections"),
]
print("\n=== Content Models ===")
for m in models:
print(f" [{m.post_type}] Endpoint: {m.rest_endpoint}")
print(f" Fields: {m.fields}")
print(f" Use: {m.use_case}")
Deployment and Operations
# === Deployment Architecture ===
# WordPress Backend:
# - Hosted on VPS / Managed WP (Kinsta, WP Engine)
# - Only accessible by editors (no public frontend)
# - Protected by .htaccess IP restriction or VPN
# - Plugins: ACF Pro, WPGraphQL, Yoast SEO, WP Webhooks
#
# Next.js Frontend:
# - Deployed on Vercel / Netlify / Cloudflare Pages
# - ISR (Incremental Static Regeneration) every 60s
# - CDN cached globally
# - On-demand revalidation via WP Webhook
#
# Webhook Flow:
# WP Post Published → WP Webhooks Plugin → POST to Vercel API
# → Vercel revalidates page → Fresh content in seconds
@dataclass
class DeployComponent:
component: str
hosting: str
url: str
purpose: str
cost: str
deploy = [
DeployComponent("WordPress", "Kinsta / VPS", "admin.example.com", "CMS Backend API", "$30-100/mo"),
DeployComponent("Next.js", "Vercel", "www.example.com", "Public frontend", "Free-$20/mo"),
DeployComponent("CDN", "Cloudflare", "cdn.example.com", "Static assets cache", "Free"),
DeployComponent("Media", "S3 + CloudFront", "media.example.com", "Image video storage", "$5-20/mo"),
DeployComponent("Search", "Algolia", "N/A (API)", "Full-text search", "Free-$29/mo"),
DeployComponent("Forms", "Formspree", "N/A (API)", "Contact forms", "Free-$10/mo"),
]
print("Deployment Architecture:")
for d in deploy:
print(f" [{d.component}] {d.hosting}")
print(f" URL: {d.url} | Purpose: {d.purpose}")
print(f" Cost: {d.cost}")
citizen_dev_tools = {
"Content": "WordPress Admin — Gutenberg Editor, ACF Fields",
"Design": "Elementor (backend preview) or Figma mockups",
"Automation": "Zapier / Make.com — connect WP to email, CRM, sheets",
"SEO": "Yoast SEO plugin — titles, meta, sitemap",
"Analytics": "Google Analytics + Search Console",
"Forms": "WPForms or Gravity Forms → REST API",
}
print(f"\n\nCitizen Developer Tools:")
for k, v in citizen_dev_tools.items():
print(f" [{k}]: {v}")
เคล็ดลับ
- show_in_rest: ทุก Custom Post Type ต้องตั้ง show_in_rest = true
- ISR: ใช้ ISR revalidate 60s ไม่ต้อง Rebuild ทั้งหมด
- Webhook: ตั้ง Webhook เมื่อ Publish แล้ว Revalidate หน้าเว็บ
- Security: ปิด WP Frontend ให้เข้าได้เฉพาะ Admin Panel
- ACF: ใช้ ACF Pro สร้าง Field ง่าย ส่งผ่าน REST API ได้
WordPress Headless คืออะไร
WordPress Backend CMS ไม่ใช้ Theme WP REST API WPGraphQL Frontend แยก Next.js Nuxt.js Astro Performance ดี ปลอดภัย Scale CDN
Citizen Developer คืออะไร
ไม่ใช่โปรแกรมเมอร์ สร้าง App ได้ Low-code No-code WordPress Admin ACF Elementor Gutenberg Zapier Make.com Content Backend Admin Panel
WP REST API ใช้อย่างไร
/wp-json/wp/v2/ Posts Pages Categories Tags Media GET POST PUT DELETE Authentication Application Password JWT Custom Post Type ACF
เลือก Frontend Framework อะไรดี
Next.js เหมาะสุด SSG SSR ISR React Community Nuxt.js Vue Astro Static เร็ว Gatsby GraphQL SvelteKit Svelte Team Skill WPGraphQL
สรุป
WordPress Headless Citizen Developer WP REST API WPGraphQL Next.js ACF Custom Post Type Content Modeling Webhook ISR CDN Deployment Production
