ทำเว็บด้วย Gatsby และใช้ Ghost เป็น CMS กันเถอะ

Published on
Gatsby
2019/12/getting-started-with-ghost-x-gatsby
Discord

สวัสดีครับ บทความนี้จะมาพูดถึงเรื่องการทำ Static Website ด้วย Gatsby กันนะครับ โดยจะใช้ Gatsby ทำเป็น Blog แต่ที่พิเศษกว่านั้น คือ เราจะไม่ใช้ Markdown ในการเขียน content ซึ่งส่วนนี้ทั้งหมด เราจะไปเขียนที่ Ghost (Blogging Platform) แทนครับ ใช้ Ghost เป็น Admin Content เหมือนๆกับเขียนบทความด้วย Wordpress เลยครับ คือมีหน้าปกติ และหน้า Admin

ฟังแล้วอาจจะยังงงๆ ว่ามันคืออะไร? มันทำงานร่วมกันยังไง? Gatsby เป็น Static Web Generator ส่วน Ghost เป็น Blogging Platform มาดูกันครับ

สรุปแบบนั้นคือ แบ่งหน้าที่ได้ดังนี้

  • Gatsby - เป็น Frontend
  • Ghost - เป็น CMS ไว้สำหรับเขียน content
  • Netlify - เป็น Web Hosting คอย build ทุกครั้งที่มี content ใหม่

รูป cover จาก https://ghost.org

สำหรับเวอร์ชั่น Video สามารถดูได้จาก https://www.youtube.com/watch?v=TadLOOKdoNo (ถ้าหากชอบ กด Subscribe ติดตามผมได้นะครับ พยายามจะเริ่มลองทำคลิปออกมาเยอะๆครับ)

Gatsby

Gatsby Image

Gatsby เป็น Static Web Generator ครับ โดยใช้ React และ Graphql ซึ่งเว็บนี้ก็ใช้ Gatsby ครับ เปลี่ยนช่วงกลางปี (ซึ่งจริงๆแพลนตั้งแต่ต้นปีนู้น แต่ได้ทำจริงๆกลางปี)

หลังจากนั้นผมก็เริ่มปรับใช้มาเรื่อยๆ ทำเว็บเล็กๆให้ลูกค้าด้วย Gatsby บ้าง เว็บบล็อกเล็กๆส่วนตัวบ้าง แล้วรู้สึกชอบ หลังๆก็เลยสนใจ อ่านบทความเกี่ยวกับ Gatsby เยอะขึ้น และรู้สึกว่ามันทำอะไรได้เยอะ ไม่จำเป็นต้อง Geek ขนาดเขียนด้วย Markdown แล้ว push ไป git แล้ว auto deploy อะไรขนาดนั้น เราสามารถแยกส่วน Content (CMS) ออกมาได้ เช่นกัน

ซึ่ง Gatsby เราสามารถแยกส่วน data หรือ source ออกมาได้ เช่นเก็บเป็น JSON, YAML, Markdown หรือแม้แต่ APIs จากนั้นก็จะทำการ transform มาเป็นรูปแบบ Graphql เป็น data layer ที่นำไปใช้ได้

ตัวอย่างบทความที่ผมเคยเขียนไว้เกี่ยวกับ Gatsby ครับ

JAMstack

JAMstack หรือก็คือ JavaScript, APIs และ Markup ซึ่งมันคือแนวคิดการทำเว็บในอีกรูปแบบหนึ่งเป็น เหมือน Static web ธรรมดานี่แหละ แต่มีความ secure และบังเอิญดันเป็น Dynamic ได้ด้วย โดยไม่ต้องมี Web Server แค่ใช้ APIs และ Markup พูดง่ายๆ คือเราอาจจะมีแค่หน้าเว็บด้วย React (Gatsby) แล้ว build เป็น Static ส่วนพวก Content เราก็ใช้ Headless CMS เช่น Contentful หรือ Ghost จากบทความนี้ หรือ APIs อื่นๆได้ JAMstack ไม่มี Web Server เราก็ใช้บริการพวก Firebase Cloud Function, Authentication, Netlify Hosting อะไรพวกนี้ เพื่อทำ Hosting หรือทำ Authentication ก็ได้เช่นกัน

Ghost

Ghost

Ghost เป็น Blogging Platform ที่เขียนด้วย Node.js / Ember / Handlebars พวกนี้ ซึ่งจริงๆแล้วเราใช้แค่ Ghost อย่างเดียว ทำเว็บ และมี Admin จัดการ content ก็ได้ปกตินะครับ (เหมือน Wordpress)

แต่ที่ผมอยากใช้ Ghost และ Gatsby คือ เราจะแยกส่วน Content CMS และส่วน Frontend แยกจากกัน

Ghost นั้นมี Ghost API ที่แยกการทำงานออกชัดเจน โดย Ghost จะมองเป็น Headless CMS คือมีส่วน Admin Content ที่ให้เราสามารถเข้าไปจัดการ Content ของเว็บได้ (อารมณ์เดียวกับ Wordpress นั้นเอง)

Headless Ghost

จะเห็นว่า Ghost นั้น มี API ทำให้ส่วน Frontend นั้นไม่จำกัดเลย เราจะใช้อะไรก็ได้ Gatsby, Hugo จะใช้ Vue (NuxtJS) ก็ไม่มีปัญหา

ซึ่งสำหรับ Gatsby ก็โชคดี ที่ทาง Ghost ทำ Gatsby Source Plugin ของ Ghost ให้เรียบร้อยแล้ว คือ Gatsby Source Ghost ซึ่งเป็น API ที่ให้เราดึง data จากเว็บ Ghost ได้

Install Ghost

ติดตั้ง Ghost CLI กันก่อนเลย

npm install ghost-cli@latest -g

จากนั้นติดตั้ง Ghost ลงบนเครื่อง (local)

ghost install local

จากนั้น เราจะได้เว็บ http://localhost:2368 และ http://localhost:2368/ghost สำหรับหน้า Admin (ครั้งแรก จะเป็นการ Setup เหมือน Wordpress เลยครับ)

Setup Ghost Screen

ก็ทำการ Setup สร้าง Account ครับ จะได้หน้า Content ลองทำการเพิ่ม Post หรือ Edit Post จากนั้นดูหน้า http://localhost:2368 เพื่อดู Content เว็บเราที่ใช้ Ghost ครับ

Start Gatsby Project

ตัว Gatsby เราจะเลือกจากตัว starter ที่ทาง Ghost ทำไว้ให้ครับชื่อ gatsby-starter-ghost

gatsby new gatsby-starter-ghost https://github.com/TryGhost/gatsby-starter-ghost.git
cd gatsby-starter-ghost

npm run dev

ตัว Starter Project ใช้ gatsby-source-ghost ซึ่งเราสามารถดูได้จากไฟล์ gatsby-config.js และไฟล์ .ghost.json จะเห็น config ประมาณนี้

{
  "development": {
    "apiUrl": "https://gatsby.ghost.io",
    "contentApiKey": "9cc5c67c358edfdd81455149d0"
  },
  "production": {
    "apiUrl": "https://gatsby.ghost.io",
    "contentApiKey": "9cc5c67c358edfdd81455149d0"
  }
}

คือตัว Gatsby ตอนนี้ใช้ Source ของ Ghost จากเว็บ https://gatsby.ghost.io นั่นเอง สิ่งที่เราต้องทำคือ เปลี่ยน apiUrl และ contentApiKey เป็น localhost ของเราครับ ซึ่งตัวนี้ เราสามารถ gen มันได้จากหน้า Admin ของ Ghost ครับ

เข้าไปที่ SETTINGS -> Integrations จากั้นเลือก Add Custom Integration

New custom integration

เราก็ก็อปเอา contentApiKey และ apiUrl ของเรา มาแทน ได้เป็นเช่น

{
  "development": {
    "apiUrl": "http://localhost:2368",
    "contentApiKey": "983fe456c5e1822f0bdf9123d9"
  },
  "production": {
    "apiUrl": "http://localhost:2368",
    "contentApiKey": "983fe456c5e1822f0bdf9123d9"
  }
}

ซึ่งผมไม่ได้แบ่งแยก environment นะครับ เลยใช้ทั้ง development และ production key เดียวกันเลย

เมื่อลอง Restart Gatsby อีกครั้ง ก็จะเห็น content จาก Ghost บน local เราแล้วครับ ซึ่งส่วนนี้เมื่อเราเห็นว่า เราสามารถดึง Content จาก Ghost มาได้แล้ว ทีนี้หน้าเว็บ เราจะปรับแต่ง CSS จะจัดการยังไง ก็ขึ้นอยู่กับเว็บแต่ละคนแล้วครับ

npm run dev

Deploy with Netlify

เมื่อเราได้เว็บแล้ว ต่อมา ก็ทำการ Deploy ไปที่ Netlify นะครับ ผมใช้การ Deploy ด้วย git และ github ฉะนั้นผมก็เลยสร้าง github repo ขึ้นมาตัวนึง และ push ขึ้นไปครับ

จากนั้นในหน้า Netlify เราก็จะสามารถเลือก Repo ของเราได้ครับ จากนั้นทำการ Deploy ครับ โดยเลือกตอน build จะให้มันสั่ง gatsby build เพื่อ build ไฟล์ และโฟลเดอร์ public สำหรับ serve พวก static web ครับ

Deploy Netlify

ลองกด Deploy site เลยครับ รอ Netlify Build ซักพักครับ . . . แต่ จะ build error Error: connect ECONNREFUSED 127.0.0.1:2368 เนื่องจาก source api เราบอกให้ Gatsby ไปดึงจาก http://127.0.01:2368 ซึ่งมันเป็นเครื่อง local เรา ตัว Netlify ดึงไม่ได้อยู่แล้ว ฉะนั้น ก็ลองเปลี่ยน .ghost.json กลับไปเป็นเริ่มต้น แล้ว commit และ push โค๊ดมาใหม่อีกทีครับ ก็จะได้

เราก็จะได้หน้าเว็บ เป็น subdomain ของ Netlify ตามชื่อ Site name ที่เราตั้งครับ เช่น https://devahoy-gatsby-ghost.netlify.com/

Create Ghost Server on Digital Ocean

ต่อมา ผมจะย้าย Ghost บนเครื่องเรา ไปวางไว้เป็น VPS Server โดยใช้ Digital Ocean นะครับ (หรือใครที่มี Ghost แล้ว ก็สามารถข้ามขั้นตอนนี้ไปได้เลย)

กดสร้าง Droplet ครับ ซึ่ง Digital Ocean มี One Click Install ครับ กดทีเดียว ได้ Ghost มาเลย

https://do.co/2PQEpZH

จากนั้นก็ access เข้าไปเครื่อง VPS นะครับ ผมได้ ip 165.22.101.202 (ซึ่งจะแตกต่างกันนะครับ)

หากใครใช้ SSH ไม่เป็น หรือไม่เคย set แนะนำทีนี้ครับ https://www.digitalocean.com/docs/droplets/how-to/connect-with-ssh/

ssh root@165.22.101.202

จากนั้นเมื่อเข้าไปได้ Digital Ocean ก็จะรัน script auto ติดตั้ง ghost ให้เรา พร้อม nginx เป็น web server แทบไม่ต้องทำอะไรก็มีเว็บได้เลย

แต่ ถ้าใครต้องการผูกโดเมน ที่มี ก็ต้องไปเซต A Record ให้ชี้มา ip ของ droplet ก่อนนะครับ และต้องรอประมาณนึงเพื่อให้ DNS มันอัพเดทเลย

ส่วนผมขอข้ามเลือกผูก domain ไปนะครับ เอาเป็นว่าใช้ ip ตรงๆเลยนี่แหละ จะมีหน้าต่างมาให้เราดูว่า เซต A Record เรียบร้อยมั้ย ถ้าโอเคกด enter เพื่อเริ่มได้เลย (ไม่มี domain จะไม่ได้เซ็ต https ให้ ซึ่งเหมาะกับการ dev เท่านั้นนะครับ ไม่ควร deploy เว็บจริงๆ โดยไม่มี https ครับ)

Ghost will prompt you for two details:

1. Your domain
 - Add an A Record -> 165.22.101.202 & ensure the DNS has fully propagated
 - Or alternatively enter http://165.22.101.202
2. Your email address (only used for SSL)

Press enter when you're ready to get started!

Note อีกครั้ง ขั้นตอนนี้คือใช้ IP droplet นะครับ เลยทำให้ไม่ได้ถูก setup https ฉะนั้นไม่ควรเอาไปใช้ production ถ้าจะใช้จริงๆ แนะนำให้ ทำ A Record และรอ DNS อัพเดทก่อน ค่อย setup หรือมา setup ghost เฉพาะ ssl/dns ทีหลังก็ได้ครับ

ต่อมาเข้าหน้า Ghost Admin ที่เราเพิ่งสร้าง droplet ครับ http://165.22.101.202/ghost

ก็ทำเหมือนตอน local เลย ไปเพิ่ม Custom Integration จากนั้น เปลี่ยน apiUrl และ contentApiKey ซะ จากนั้น commit และ push code มาใหม่ Netlify ก็จะทำการ build และ deploy โดยดึง data source จาก ghost ที่รันบน Digital Ocean ละครับ

ทดสอบ แก้ไขบทความบน Digital Ocean แล้วสั่ง Netlify มัน rebuild ใหม่ครับ Deploys -> Trigger deploy -> Deploy site

แต่ๆ ถ้าเราเพิ่มเนื้อหาใน Ghost แล้วต้องมากด deploy site ใน Netlify ทุกครั้งก็ไม่ดีแน่ พวก task routine พวกนี้เราไม่ต้องเสียเวลาทำครับ

Netlify Webhook

Netlify และ Ghost มี Webhook ครับ ไปที่ Deploy Settings -> Add build hook ครับ เราจะได้ url webhook ครับ เช่น https://api.netlify.com/build_hooks/5e0648c2a2e9cd72f2f6aaa3

ทีนี้ก็ไปหน้า Ghost Admin ไปที่ Settings -> Integrations -> เลือก custom integration ที่เราสร้างไว้ จากนั้นกด Add webhook ครับ

  • Name - ตั้งชื่อตามใจ
  • Event - เลือกเป็น site changed (ซึ่งเราสามารถเลือกเป็น event อื่นๆได้ เช่น มี new post ถึงจะ build ใหม่ เป็นต้น)
  • Target URL - ใส่เป็น webhook ที่ได้จาก Netlify
Add Webhook

ตอนนี้ Netlify ก็ build ทุกครั้งที่เนื้อหาเราอัพเดทแล้ว

Duplicate content && Gatsby Caching

ต่อมาครับ หลายคนอาจจะงงๆ ยังงี้เราไม่มี duplicate content หรอ เนื้อหาเหมือนกัน 2 เว็บคือ ghost และ gatsby ก็ตอบว่าใช่ครับ แต่เราสามารถเลือกให้ ghost เป็น private ได้ครับ

ไปที่ Settings -> General -> เลือก Make this site private

ทีนี้ตัว Ghost ก็จะเข้าไม่ได้แล้ว ต้องใส่ Password (นอกจากหน้า Admin) รวมถึงก็จะไม่ถูก index จาก Google ก็จะไม่ถือว่าเป็น duplicate content แล้วครับ

สุดท้ายครับ การทำ static file เราจำเป็นต้องพึ่งพวก caching ครับ เพื่อให้ไม่ต้องโหลดซ้ำๆ แต่ว่าใน Gatsby มีบางส่วนที่ถ้าเราไป cache มันไว้ หน้าเว็บก็จะไม่สามารถอัพเดทได้ (กรณี rebuild user จะไม่เห็นการเปลี่ยนแปลง) นอกจากจะ clear cache ครับ เพราะมีพวก service worker / offline อยู่ ฉะนั้น เราจำเป็นต้องเลือก cache เฉพาะไฟล์ที่จำเป็น (ที่ควรโหลดทุกครั้ง เพื่อเช็ค content ว่าเปลี่ยนมั้ย เช่น /sw.js) นอกจากนั้นก็คือไฟล์ html ทั้งหมด และ page-data/*.json ครับ ซึ่งการตั้งค่าพวก caching ของ Netlify เราทำผ่านไฟล์ _headers ครับ โดยตัว starter ก็มีมาให้แล้ว เราก็แค่แก้ไขไฟล์ static/_headers โดยเพิ่มด้านล่างลงไป

/sw.js   # Gatsby's default service worker file path
  Cache-Control: no-cache

/*.html
  Cache-Control: no-cache

/public/page-data/*.json
  Cache-Control: no-cache

จากนั้น commit และ push code ไปใหม่ ทำการ edit content ที่ ghost รอ Netlify build ใหม่ เนื้อหาเราก็จะถูกอัพเดทครับ

สุดท้าย

ก็หวังว่าบทความนี้จะเป็นประโยชน์ และเป็นอีกหนึ่งแนวทางที่น่าสนใจในการทำพวก CMS เว็บ โดยแยก CMS กับ Frontend ออกจากกัน ก็ลองไปเล่นกันดูนะครับ มีอะไรให้เล่นอีกเยอะ ลองปรับแก้ starter เอง ทำหน้าอื่นๆเอง source data อื่นๆ พวกนี้ครับ

Happy Coding ❤️

References

Buy Me A Coffee
Authors
Discord