今天要接著介紹網頁開發很重要的一環, data fetch。很多時候我們開發功能時,往往需要從外部拿取資料,要如何拿取資料就是很重要的問題。這一篇會來介紹如何在 next 13 裡做 data fetch,以及一些要注意的地方。
fetch
apifetch
api,相信大家應該都很熟悉 fetch
,在 next 上可以在 server side 使用 fetch 來抓取資料。cache
api,讓第三方套件的資料一樣能被快取起來。next 官方建議盡量在 server 上做 data fetch ,因此本篇會著重在如何在 server 上抓取資料,client 端的部分則會在講 route handler 或第三方套件時再介紹。
next 支援在 server side 使用 fetch
api,並且將 fetch
強化,讓 fetch
有更多功能,幫助我們優化抓取資料的流程。
React (不是 next )強化了原有的 fetch api,在 fetch 上加入了“記憶”功能。
當發送一個 api 請求時,fetch 會記憶這個 request 的形式,當下次有同樣路徑、同樣格式的 request 要呼叫時,fetch 就會從記憶體中拉出上次 response 回來的結果,並回傳給呼叫的位置。
簡單講一下背後是如何運作的,可以看官方提供的這張圖:
當第一次 request 送出時,記憶體沒有資料,會在 cache 裡記上MISS
,並 call api,拿到資料後將資料丟入記憶體,並在 cache 記上SET
,同時送資料給呼叫的地方。
後續每當有地方要 call api 時(不一定是同個 component),只要是相同的 request ,就會在 cache 上記 HIT
,並回傳記憶體的資料。
要注意的是,這個功能是有些限制的:
GET
方法能使用next 在 React 的基礎之上給 fetch 加強了兩個新功能,其中一個是 cache
,透過這個快取設定可以將資料保留在記憶體中,跟前一點有異曲同工之妙,但不同的是:
POST
方法GET
,透過 cache 也可以使用在 POST
方法上。cache 可以幫助我們記錄著資料,不需要每一次有 request 時就發一次 api,減少伺服器的負擔。
在 fetch 後面的 option 上加上 cache: <setting words>
即可,其中 force-cache
是預設值,可以省略。
// force-cache is default
const data = await fetch('https://example.com/api', {cache: 'force-cache'})
其他的設定可以參考官方文件,這邊就容我先跳過。
revalidation (重新驗證?)是 next 給 fetch 的其中一個強化,用於 重新 fetch 資料。透過這個功能,可以確保取得的資料是我們需要且最新的,適合需要變動的資料。
revalidation 有兩種形式:
const resp = await fetch(api, { next: { revalidate: 3600 } })
MISS
、SET
、HIT
等),因此可以依據 tag 來要求做 revalidation;此外,也可以依據路徑的變動做 revalidation。export default async function Page() {
const res = await fetch('https://...', { next: { tags: ['collection'] } })
const data = await res.json()
// ...
}
實做一個 route handler 來處理 revalidation,這邊是呼叫 next 提供的 revalidateTag()
api:
import { NextRequest, NextResponse } from 'next/server'
import { revalidateTag } from 'next/cache'
// e.g a webhook to `your-website.com/api/revalidate?tag=collection&secret=<token>`
export async function POST(request: NextRequest) {
const secret = request.nextUrl.searchParams.get('secret')
const tag = request.nextUrl.searchParams.get('tag')
if (secret !== process.env.MY_SECRET_TOKEN) {
return NextResponse.json({ message: 'Invalid secret' }, { status: 401 })
}
if (!tag) {
return NextResponse.json({ message: 'Missing tag param' }, { status: 400 })
}
revalidateTag(tag)
return NextResponse.json({ revalidated: true, now: Date.now() })
}
import { NextRequest, NextResponse } from 'next/server'
import { revalidatePath } from 'next/cache'
export async function POST(request: NextRequest) {
const path = request.nextUrl.searchParams.get('path')
if (!path) {
return NextResponse.json({ message: 'Missing path param' }, { status: 400 })
}
revalidatePath(path)
return NextResponse.json({ revalidated: true, now: Date.now() })
如果今天需要用第三方套件來呼叫 api,像是 mongoose 這類連結資料庫的套件,有他們自己的呼叫方式,此時就不能使用 fetch 。那要如何實現類似記憶的功能呢?這時可以使用 react 提供的 cache
function。
這個 cache
function 可以應用在第三方套件呼叫 api 的 function 上,進而將資料記憶在快取裡,減少 api 的重複呼叫。
import { cache } from 'react';
export const getItem = cache(async (id: string) => {
const item = await db.item.findUnique({ id })
return item
})
以上,是關於 fetch data 的內容,在做網頁開發時必定繞不開這個課題,在 server 上做完 data fetch ,將可使 client 端減少負擔,而為了讓 server 上的資料抓取效能更好, React 和 next 設計了一連串的機制來處理資料抓取。