ข้ามไปยังเนื้อหา

Data Fetching

component script ของ .astro รันบน server เราจึง fetch ข้อมูลภายนอกด้วยวิธีที่ตรงไปตรงมาที่สุด คือ await fetch() แบบ top-level ไม่มี useEffect, ไม่มี loading state, ไม่มี round-trip ไปฝั่ง client ข้อมูล resolve เสร็จก่อนที่ HTML จะมีอยู่ด้วยซ้ำ

src/pages/products.astro
---
const res = await fetch('https://api.example.com/products');
if (!res.ok) throw new Error(`API returned ${res.status}`);
const products = await res.json();
---
<ul>
{products.map((p) => <li>{p.name} — ${p.price}</li>)}
</ul>

pattern เดียวกันใช้ได้กับ REST API, database client, หรือ SDK ของ headless CMS เพราะรันบน server ความลับ (API key) จึงอยู่ฝั่ง server ไม่ถูกส่งไป browser

ตอนไหน ที่ fetch นั้นรัน ขึ้นกับ rendering mode ของหน้า และนี่คือเรื่องสำคัญที่สุดที่ต้องเข้าใจเกี่ยวกับข้อมูลใน Astro:

  • หน้า static (ค่า default)fetch รัน ครั้งเดียว ตอน build response ถูกอบเข้าไปในไฟล์ HTML ทุกคนที่เข้ามาได้ snapshot เดียวกันจนกว่าจะ build ใหม่ เหมาะกับ product list, blog index, docs คือข้อมูลที่เปลี่ยนตามรอบ deploy
  • หน้า on-demand (prerender = false)fetch รัน ต่อ request บน server แต่ละคนได้ข้อมูลสดและ personalize ได้ ทั้ง user ปัจจุบัน, inventory สด, ผลค้นหา ต้องใช้ adapter (ดูโมดูล rendering)
---
// หน้านี้ render ต่อ request ดังนั้น fetch จึงสดทุกครั้งที่เข้ามา
export const prerender = false;
const res = await fetch('https://api.example.com/inventory');
const inventory = await res.json();
---
<p>เหลือในสต็อกตอนนี้ {inventory.inStock} ชิ้น</p>

แบบทดสอบในใจ: ข้อมูลนี้ต้องสดต่อผู้ชมแต่ละคน หรือ snapshot ตอน build ก็พอ? snapshot พอ → ปล่อยให้ static ไว้ (เร็ว, cache ได้) ต้องสด → เปลี่ยน route นั้นให้ render แบบ on-demand การรีบไปใช้ on-demand ทั้งที่ snapshot ตอน build ก็พอ เป็นวิธีที่คนทิ้งความเร็วของ Astro ไปเปล่า ๆ บ่อยที่สุด

flowchart TB
  q["ข้อมูลเปลี่ยนต่อ request ไหม?"]
  q -->|ไม่| static["Static: fetch รันครั้งเดียวตอน build อบเข้า HTML"]
  q -->|ใช่| ondemand["On-demand: prerender = false, fetch รันต่อ request"]
  static --> cdn["เสิร์ฟจาก CDN เหมือนกันทุกคน"]
  ondemand --> server["render บน server ต่อผู้ชม"]
fetch รันตอนไหน?

headless CMS (Contentful, Sanity, Storyblok, WordPress API) ก็เป็นแค่แหล่งข้อมูลอีกแหล่ง fetch ใน component script ได้เลย บ่อยครั้งผ่าน SDK ของ CMS แล้ว map response ลง template สำหรับ content ที่เราเป็นเจ้าของและแก้เป็นไฟล์เอง ให้ใช้ content collection ที่มี loader ส่วน content ที่จัดการในระบบภายนอก ให้ fetch เอาเอง (หรือใช้ community loader ที่ห่อ CMS ไว้หลัง collections API) ไม่ว่าทางไหนโค้ดหน้าเพจก็หน้าตาเหมือนกัน คือดึงข้อมูลบน server แล้ว render HTML

โค้ด Astro รุ่นเก่าใช้ Astro.glob() ดึงไฟล์ในเครื่องหลายไฟล์ (Markdown ทั้งหมดในโฟลเดอร์) พร้อมกัน ตอนนี้ Astro.glob() ยังใช้ได้อยู่แต่ ถูกแทนที่ด้วย content collection ซึ่งให้พลัง “โหลดหลายไฟล์” แบบเดียวกัน บวก schema validation และ type ที่ generate ให้ สำหรับ content ในเครื่อง ให้ประกาศ collection ด้วย glob loader แทนที่จะไปคว้า Astro.glob() ส่วน fetch ดิบ ๆ เก็บไว้ใช้กับข้อมูลภายนอกจริง ๆ

fetch ข้อมูลภายนอกใน .astro component ยังไง?
บนหน้า static (default) top-level fetch รันตอนไหน?
ต้องการข้อมูลสดต่อผู้ชมบนหน้าหนึ่ง ต้องตั้งอะไร?
สำหรับโหลดไฟล์ Markdown ในเครื่องหลายไฟล์ อะไรดีกว่า Astro.glob()?