روش‌های دریافت داده در Next.js

دریافت داده بخش اصلی هر اپلیکیشنی به شمار می‌آید. در این مقاله قصد داریم تا نحوه fetch، cache و revalidate کردن داده در React و Next.js را باهم بررسی کنیم.

چهار روش در Next.js برای دریافت کردن داده وجود دارد که عبارتند از:

  • بر روی سرور، با fetch
  • بر روی سرور، با کتابخانه‌های third-party
  • روی کلاینت، از طریق یک Route Handler
  • روی کلاینت، با کتابخانه‌های third-party

دریافت داده بر روی سرور با استفاده از fetch در Next.js

کاری که Next.js انجام می‌دهد این است که fetch Web API native را extend می‌کند تا به ما اجازه دهد رفتار caching و revalidating را برای هر درخواست fetch بر روی سرور پیکربندی کنیم. کاری که React انجام می‌دهد این است که

fetch
fetchرا extend می‌کند تا حین رندر کردن درخت کامپوننت React، درخواست‌های fetch به طور خودکار به خاطر سپرده شوند.

ما می‌توانیم از

fetch
fetch همراه با
async
async/
await
awaitدر سرور کامپوننت‌ها، در Route Handlerها و همینطور در Server Actionها استفاده نماییم. به عنوان مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app/page.tsx
async function getData() {
const res = await fetch('https://api.example.com/...')
// The return value is *not* serialized
// You can return Date, Map, Set, etc.
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch data')
}
return res.json()
}
export default async function Page() {
const data = await getData()
return <main></main>
}
app/page.tsx async function getData() { const res = await fetch('https://api.example.com/...') // The return value is *not* serialized // You can return Date, Map, Set, etc. if (!res.ok) { // This will activate the closest `error.js` Error Boundary throw new Error('Failed to fetch data') } return res.json() } export default async function Page() { const data = await getData() return <main></main> }
app/page.tsx

async function getData() {
  const res = await fetch('https://api.example.com/...')
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.
 
  if (!res.ok) {
    // This will activate the closest `error.js` Error Boundary
    throw new Error('Failed to fetch data')
  }
 
  return res.json()
}
 
export default async function Page() {
  const data = await getData()
 
  return <main></main>
}

بهتر است به این نکات توجه داشته باشیم که:

  • Next.js توابع مفیدی را ارائه می‌کند که ممکن است هنگام fetch کردن داده در سرور کامپوننت‌هایی مانند cookies و headers به آن‌ها نیاز داشته باشیم. این‌ها باعث می‌شوند تا مسیر به صورت پویا رندر شود، زیرا به اطلاعات زمان درخواست متکی هستند.
  • در Route handlerها، درخواست‌های
    fetch
    fetchدر حافظه ذخیره نمی‌شوند، زیرا Route Handler‌ها بخشی از درخت کامپوننت React نیستند.
  • برای استفاده از
    async
    async/
    await
    awaitدر یک سرور کامپوننت با تایپ اسکریپت، باید از نسخه ۵٫۱٫۳ یا بالاتر تایپ اسکریپت و نسخه ۱۸٫۲٫۸ یا بالاتر
    @types/react
    @types/reactاستفاده کنیم.

Caching داده‌ها

Caching داده‌ها را ذخیره می‌کند، بنابراین نیازی نیست در هر درخواستی برای دریافت مجدد داده‌ها، آن‌ها را از منبع داده‌ای که داریم fetch کنیم.

به‌طور پیش‌فرض، Next.js به صورت خودکار مقادیر بازگشتی

fetch
fetch را در حافظه cache بر روی سرور ذخیره می‌کند. این بدان معنی است که ما می‌توانیم داده‌ها را در زمان ساخت یا زمان درخواست fetch کنیم، cache کنیم و در هر درخواست داده دوباره از آن استفاده نماییم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 'force-cache' is the default, and can be omitted
fetch('https://...', { cache: 'force-cache' })
// 'force-cache' is the default, and can be omitted fetch('https://...', { cache: 'force-cache' })
// 'force-cache' is the default, and can be omitted
fetch('https://...', { cache: 'force-cache' })

درخواست‌های

fetch
fetchکه از متد
POST
POSTاستفاده می‌کنند هم به‌طور خودکار ذخیره می‌شوند. مگر اینکه داخل یک Route Handler باشد که از متد
POST
POST استفاده می‌کند، در این صورت ذخیره نمی‌شود.

Revalidating داده‌ها

Revalidation در Next.js فرآیند پاکسازی حافظه cache و دریافت کردن مجدد آخرین داده ها می‌باشد. این موضوع زمانی مفید است که داده‌ها تغییر می‌کند و ما می‌خواهیم مطمئن شویم که آخرین اطلاعات را به کاربران نمایش می‌دهیم.

داده‌های ذخیره شده را می‌توانیم به دو روش revalidate کنیم:

  • Revalidation مبتنی بر زمان: این روش پس از گذشت مدت زمان معینی، داده‌ها را به‌طور خودکار revalidate می‌کند. استفاده از این روش برای داده‌هایی که به ندرت تغییر می‌کنند و به روز بودن آن‌ها ‌چندان مهم نیست، بسیار مفید می‌باشد.
  • Revalidation مبتنی بر تقاضا: این روش، revalidate داده‌ها را به صورت دستی و بر اساس یک رویداد (به عنوان مثال ارسال فرم) تنظیم می‌کند. Revalidation مبتنی بر تقاضا می‌تواند از رویکرد tag-based یا path-based برای revalidate گروه‌های داده‌ها به‌طور هم‌زمان استفاده نماید. این موضوع زمانی مفید است که می‌خواهیم مطمئن شویم آخرین داده‌ها را در اسرع وقت به نمایش می‌گذاریم.

Revalidation مبتنی بر زمان

برای revalidate داده‌ها در یک بازه زمانی معین، می‌توانیم از گزینه

next.revalidate
next.revalidateمربوط به
fetch
fetchبرای تنظیم طول عمر حافظه cache یک منبع (در ثانیه) استفاده کنیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
fetch('https://...', { next: { revalidate: 3600 } })
fetch('https://...', { next: { revalidate: 3600 } })
fetch('https://...', { next: { revalidate: 3600 } })

از طرف دیگر، برای revalidate همه درخواست‌های

fetch
fetch در یک segment مسیر، می‌توانیم از گزینه‌های پیکربندی Segment استفاده نماییم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
layout.js | page.js
export const revalidate = 3600 // revalidate at most every hour
layout.js | page.js export const revalidate = 3600 // revalidate at most every hour
layout.js | page.js

export const revalidate = 3600 // revalidate at most every hour

اگر چندین درخواست fetch در یک مسیر رندر شده استاتیک داشته باشیم و هر کدام فرکانس revalidation متفاوتی داشته باشند؛ در این صورت کم‌ترین زمان برای همه درخواست‌ها مورد استفاده قرار می‌گیرد. اما برای مسیرهای رندر شده به صورت پویا، هر درخواست

fetch
fetchمستقلاً revalidate می‌شود.

Revalidation مبتنی بر تقاضا

می‌توانیم داده‌ها را بر حسب تقاضا توسط path (

revalidatePath
revalidatePath) یا توسط tag حافظه cache (
revalidateTag
revalidateTag) در داخل یک Server Action یا Route Handler، مجددا revalidated کنیم.

Next.js یک سیستم cache tagging برای باطل کردن درخواست‌های

fetch
fetchدر مسیرها دارد.

  1. هنگام استفاده از
    fetch
    fetch، این گزینه را داریم که ورودی‌های cache را با یک یا چند تگ برچسب‌گذاری کنیم.
  2. سپس، می‌توانیم
    revalidateTag
    revalidateTagرا فراخوانی کنیم تا از این طریق همه ورودی‌های مرتبط با آن تگ را revalidate نماییم.

به عنوان مثال، درخواست fetch زیر تگ cache

collectio
collectio را اضافه می‌کند:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app/page.tsx
export default async function Page() {
const res = await fetch('https://...', { next: { tags: ['collection'] } })
const data = await res.json()
// ...
}
app/page.tsx export default async function Page() { const res = await fetch('https://...', { next: { tags: ['collection'] } }) const data = await res.json() // ... }
app/page.tsx

export default async function Page() {
  const res = await fetch('https://...', { next: { tags: ['collection'] } })
  const data = await res.json()
  // ...
}

سپس می‌توانیم این

fetch
fetchبرچسب‌گذاری شده با
collection
collectionرا با فراخوانی
revalidateTag
revalidateTagدر یک Server Action مجدداً revalidate کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app/actions.ts
'use server'
import { revalidateTag } from 'next/cache'
export default async function action() {
revalidateTag('collection')
}
app/actions.ts 'use server' import { revalidateTag } from 'next/cache' export default async function action() { revalidateTag('collection') }
app/actions.ts

'use server'
 
import { revalidateTag } from 'next/cache'
 
export default async function action() {
  revalidateTag('collection')
}

مدیریت خطا و  revalidation

اگر هنگام تلاش برای revalidate کردن داده‌ها خطایی رخ بدهد، در این صورت آخرین داده‌هایی که با موفقیت تولید شده بودند همچنان از حافظه cache ارائه می‌شوند. سپس درخواست بعدی، Next.js مجدداً داده‌ها را revalidate می‌کند.

انصراف از Data Caching

درخواست‌های

fetch
fetchدر حافظه cache ذخیره نمی‌شوند اگر:

  • cache: 'no-store'
    cache: 'no-store'به درخواست‌های
    fetch
    fetchاضافه شود.
  • گزینه
    revalidate: 0
    revalidate: 0به درخواست‌های
    fetch
    fetch منفرد اضافه ‌شود.
  • درخواست
    fetch
    fetch داخل یک Router Handler باشد که از متد
    POST
    POSTاستفاده می‌کند.
  • درخواست
    fetch
    fetch پس از استفاده از
    headers
    headersیا
    cookies
    cookiesانجام شود.
  • گزینه
    const dynamic = 'force-dynamic'
    const dynamic = 'force-dynamic'در segment مسیر استفاده شود.
  • گزینه
    fetchCache
    fetchCacheدر segment مسیر به‌طور پیش‌فرض به گونه‌ای پیکربندی شده باشد که از حافظه cache عبور کند.
  • درخواست
    fetch
    fetch از هدرهای
    Authorization
    Authorizationیا
    Cookie
    Cookieاستفاده کند و یک درخواست ذخیره نشده بالای آن در درخت کامپوننت وجود داشته باشد.

درخواست‌های fetch منفرد

برای انصراف از ذخیره‌سازی برای درخواست‌های

fetch
fetchمنفرد، می‌توانیم گزینه
cache
cacheرا در
fetch
fetch بر روی
'no-store'
'no-store'تنظیم کنیم. به این ترتیب داده‌ها به صورت پویا و در هر درخواست، دریافت می‌شوند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
layout.js | page.js
fetch('https://...', { cache: 'no-store' })
layout.js | page.js fetch('https://...', { cache: 'no-store' })
layout.js | page.js

fetch('https://...', { cache: 'no-store' })

درخواست‌های fetch چندگانه

اگر چندین درخواست

fetch
fetch در یک segment مسیر داشته باشیم (مثلاً یک Layout یا Page)، می‌توانیم رفتار ذخیره‌سازی تمام درخواست‌های داده در segment را با استفاده از گزینه‌های پیکربندی Segment پیکربندی کنیم.

با این حال، توصیه می‌شود که رفتار ذخیره‌سازی برای هر درخواست

fetch
fetchرا به‌صورت جداگانه پیکربندی کنیم. این کار کمک می‌کند تا کنترل دقیق‌تری بر رفتار caching داشته باشیم.

دریافت داده بر روی سرور با استفاده از کتابخانه‌های third-party در Next.js

در مواردی که از کتابخانه third-party استفاده می‌کنیم که

fetch
fetchرا پشتیبانی نمی‌کند یا در معرض نمایش قرار نمی‌دهد (به عنوان مثال، یک پایگاه داده، CMS یا ORM کلاینت)، می‌توانیم رفتار caching و revalidating آن درخواست‌ها را با استفاده از گزینه پیکربندی Segment مسیر و تابع
cache
cacheReact پیکربندی نماییم.

اینکه داده‌ها در حافظه cache ذخیره می‌شوند یا نه به این موضوع بستگی دارد که Segment مسیر به صورت استاتیک رندر شده است یا به صورت پویا. اگر Segment استاتیک (پیش‌فرض) باشد، خروجی درخواست به‌عنوان بخشی از Segment مسیر در حافظه cache ذخیره شده و revalidate می‌شود. اگر Segment پویا باشد، خروجی درخواست در حافظه cache ذخیره نمی‌شود و در هر درخواستی که Segment رندر می‌شود، دوباره fetch می‌گردد.

همچنین می‌توانیم از API آزمایشی

unstable_cache
unstable_cacheاستفاده کنیم.

به عنوان مثال:

  • تابع
    cache
    cacheReact برای به خاطر سپردن درخواست‎‌های داده، مورد استفاده قرار می‌گیرد.
  • گزینه
    revalidate
    revalidateدر بخش Layout و Page روی
    ۳۶۰۰
    ۳۶۰۰تنظیم شده است، به این معنی که داده‌ها حداکثر هر یک ساعت یک بار در حافظه cache ذخیره شده و revalidate می‌شوند.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app/utils.ts
import { cache } from 'react'
export const getItem = cache(async (id: string) => {
const item = await db.item.findUnique({ id })
return item
})
app/utils.ts import { cache } from 'react' export const getItem = cache(async (id: string) => { const item = await db.item.findUnique({ id }) return item })
app/utils.ts

import { cache } from 'react'
 
export const getItem = cache(async (id: string) => {
  const item = await db.item.findUnique({ id })
  return item
})

اگرچه تابع

getItem
getItemدو بار فراخوانی می‌شود، اما تنها یک query در پایگاه داده انجام می‌گردد.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app/item/[id]/layout.tsx
import { getItem } from '@/utils/get-item'
export const revalidate = 3600 // revalidate the data at most every hour
export default async function Layout({
params: { id },
}: {
params: { id: string }
}) {
const item = await getItem(id)
// ...
}
app/item/[id]/layout.tsx import { getItem } from '@/utils/get-item' export const revalidate = 3600 // revalidate the data at most every hour export default async function Layout({ params: { id }, }: { params: { id: string } }) { const item = await getItem(id) // ... }
app/item/[id]/layout.tsx

import { getItem } from '@/utils/get-item'
 
export const revalidate = 3600 // revalidate the data at most every hour
 
export default async function Layout({
  params: { id },
}: {
  params: { id: string }
}) {
  const item = await getItem(id)
  // ...
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
app/item/[id]/page.tsx
import { getItem } from '@/utils/get-item'
export const revalidate = 3600 // revalidate the data at most every hour
export default async function Page({
params: { id },
}: {
params: { id: string }
}) {
const item = await getItem(id)
// ...
}
app/item/[id]/page.tsx import { getItem } from '@/utils/get-item' export const revalidate = 3600 // revalidate the data at most every hour export default async function Page({ params: { id }, }: { params: { id: string } }) { const item = await getItem(id) // ... }
app/item/[id]/page.tsx

import { getItem } from '@/utils/get-item'
 
export const revalidate = 3600 // revalidate the data at most every hour
 
export default async function Page({
  params: { id },
}: {
  params: { id: string }
}) {
  const item = await getItem(id)
  // ...
}

دریافت داده بر روی کلاینت با استفاده از Route Handlerها در Next.js

اگر نیاز به fetch کردن داده‌ها در یک کلاینت کامپوننت داریم، می‌توانیم یک Route Handler را از کلاینت فراخوانی کنیم. Route Handlerها روی سرور اجرا می‌شوند و داده‌ها را به کلاینت retrun می‌کنند. این کار زمانی مفید است که نمی‌خواهیم اطلاعات حساسی مانند توکن‌های API را در اختیار کلاینت قرار دهیم.

نکته‌ای که باید درمورد سرور کامپوننت‌ها و Route Handlerها به آن توجه کنیم این است که سرور کامپوننت‌ها روی سرور رندر می‌شوند، از این رو برای دریافت کردن داده‌ها نیازی به فراخوانی Route Handler از یک سرور کامپوننت نداریم. در عوض، می‌توانیم داده‌ها را مستقیماً در داخل سرور کاپموننت دریافت نماییم.

دریافت داده بر روی کلاینت با استفاده از کتابخانه‌های third-party در Next.js

همچنین می‌توانیم با استفاده از یک کتابخانه third-party مانند SWR یا TanStack Query، داده‌ها را روی کلاینت دریافت کنیم. این کتابخانه‌ها برای به خاطر سپردن درخواست‌ها، caching، revalidating و mutating داده‌ها APIهای خود را ارائه می‌کنند.

درمورد APIهای آینده باید به این موضوع توجه کنیم که use یک تابع React است که promiseای را که توسط یک تابع return می‌شود را می‌پذیرد وآن را مدیریت می‌کند. در حال حاضر قرار دادن fetch در تابع

use
use در کلاینت کامپوننت‌ها توصیه نمی‌شود و ممکن است باعث ایجاد رندرهای مجدد شود.

دیدگاه‌ها:

افزودن دیدگاه جدید