Fetch ใน Server Components
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”Server Component เป็น async function จึง await fetch หรือ query database ได้ ตรง ๆ ในตัว component ไม่ต้องมี useEffect ไม่ต้องมี loading state และไม่ต้องยิงจาก client ไปหา API
Fetch ตรง ๆ ใน async component
หัวข้อที่มีชื่อว่า “Fetch ตรง ๆ ใน async component”ใน App Router component รันบน server เรา fetch ตรงจุดที่ render และ data ไม่หลุดเข้า client bundle จนกว่าจะส่ง HTML ออกไป
export default async function ProductsPage() { // Runs on the server — the API key and response stay server-side const res = await fetch('https://api.example.com/products') const products = await res.json() return <ProductList products={products} />}เพราะ code รันบน server เราจึงคุย database หรือ ORM ได้ตรง ๆ ไม่ต้องมี API route คั่นกลาง
import { db } from '@/lib/db'
export default async function UsersPage() { // Query the database straight from the Server Component const users = await db.user.findMany() return <UserList users={users} />}Sequential vs. parallel เลี่ยง waterfall
หัวข้อที่มีชื่อว่า “Sequential vs. parallel เลี่ยง waterfall”ถ้า await request แรกแล้วค่อย await ตัวถัดไป ตัวที่สองจะเริ่มไม่ได้จนกว่าตัวแรกจะเสร็จ นั่นคือ waterfall เมื่อ request ไม่ได้ขึ้นต่อกัน ให้ยิงพร้อมกันด้วย Promise.all
async function getUser(id: string) { const res = await fetch(`https://api.example.com/users/${id}`) return res.json()}
async function getPosts(id: string) { const res = await fetch(`https://api.example.com/users/${id}/posts`) return res.json()}
export default async function Dashboard() { // Parallel: both requests fire at once, no waterfall const [user, posts] = await Promise.all([getUser('1'), getPosts('1')]) return <Profile user={user} posts={posts} />}Dedupe ด้วย Request Memoization และ React.cache()
หัวข้อที่มีชื่อว่า “Dedupe ด้วย Request Memoization และ React.cache()”ภายใน render pass เดียว Next.js ทำ memoize fetch ที่เหมือนกันให้อัตโนมัติ เรียก URL เดิมจากสาม component แต่ network เห็นแค่ request เดียว ส่วนงานที่ไม่ใช่ fetch เช่น query database ให้ห่อด้วย cache() ของ React เพื่อ dedupe แบบ per-request เหมือนกัน
import { cache } from 'react'import { db } from '@/lib/db'
// Called by many components in one render — the query runs once per requestexport const getUser = cache(async (id: string) => { return db.user.findUnique({ where: { id } })})จุดที่ไม่ควร fetch
หัวข้อที่มีชื่อว่า “จุดที่ไม่ควร fetch”Client Component ใช้ await ในตัวเองไม่ได้ และห้ามแตะ database ตรง ๆ ต้องเข้าถึง data ผ่าน Route Handler หรือ data library ที่รันบน server แทน
'use client'import { useState } from 'react'
export function SearchBox() { const [results, setResults] = useState([]) async function onSearch(q: string) { // Client components fetch through a Route Handler, never the DB const res = await fetch(`/api/search?q=${q}`) setResults(await res.json()) } return <input onInput={(e) => onSearch(e.currentTarget.value)} />}flowchart LR A["Request"] --> B["await getUser()"] B --> C["await getPosts()"] C --> D["Render (late)"] A --> E["Promise.all"] E --> F["getUser() + getPosts() together"] F --> G["Render (sooner)"]