جلب البيانات، التخزين المؤقت، وإعادة التحقق

يعد جلب البيانات جزءًا أساسيًا من أي تطبيق. تتناول هذه الصفحة كيفية جلب البيانات، تخزينها مؤقتًا، وإعادة التحقق منها في React وNext.js.

هناك أربع طرق لجلب البيانات:

  1. على الخادم باستخدام fetch
  2. على الخادم باستخدام مكتبات الطرف الثالث
  3. على العميل عبر Route Handler
  4. على العميل باستخدام مكتبات الطرف الثالث.

جلب البيانات على الخادم باستخدام fetch

يمتد Next.js واجهة برمجة تطبيقات fetch الأصلية للسماح لك بتكوين سلوك التخزين المؤقت وإعادة التحقق لكل طلب جلب على الخادم. يمتد React fetch لتلقائيًا تذكر طلبات الجلب أثناء عرض شجرة مكونات React.

يمكنك استخدام fetch مع async/await في مكونات الخادم، في Route Handlers، وفي Server Actions.

على سبيل المثال:

async function getData() {
  const res = await fetch('https://api.example.com/...')
  // القيمة المرجعة *ليست* متسلسلة
  // يمكنك إرجاع Date، Map، Set، إلخ.

  if (!res.ok) {
    // سيؤدي هذا إلى تنشيط أقرب حد خطأ `error.js`
    throw new Error('فشل في جلب البيانات')
  }

  return res.json()
}

export default async function Page() {
  const data = await getData()

  return <main></main>
}
async function getData() {
  const res = await fetch('https://api.example.com/...')
  // القيمة المرجعة *ليست* متسلسلة
  // يمكنك إرجاع Date، Map، Set، إلخ.

  if (!res.ok) {
    // سيؤدي هذا إلى تنشيط أقرب حد خطأ `error.js`
    throw new Error('فشل في جلب البيانات')
  }

  return res.json()
}

export default async function Page() {
  const data = await getData()

  return <main></main>
}

معلومة مفيدة:

  • يوفر Next.js دوال مساعدة قد تحتاجها عند جلب البيانات في مكونات الخادم مثل cookies وheaders. سيؤدي هذا إلى عرض المسار ديناميكيًا حيث تعتمد على معلومات وقت الطلب.
  • في Route Handlers، لا يتم تذكر طلبات fetch حيث أن Route Handlers ليست جزءًا من شجرة مكونات React.
  • لاستخدام async/await في مكون خادم مع TypeScript، ستحتاج إلى استخدام TypeScript 5.1.3 أو أعلى و @types/react 18.2.8 أو أعلى.

التخزين المؤقت للبيانات

يخزن التخزين المؤقت البيانات حتى لا تحتاج إلى إعادة جلبها من مصدر البيانات في كل طلب.

افتراضيًا، يقوم Next.js تلقائيًا بتخزين القيم المرجعة من fetch في ذاكرة التخزين المؤقت للبيانات على الخادم. هذا يعني أنه يمكن جلب البيانات في وقت البناء أو وقت الطلب، تخزينها مؤقتًا، وإعادة استخدامها في كل طلب بيانات.

// 'force-cache' هو الافتراضي، ويمكن حذفه
fetch('https://...', { cache: 'force-cache' })

يتم أيضًا تخزين طلبات fetch التي تستخدم طريقة POST تلقائيًا. ما لم تكن داخل Route Handler تستخدم طريقة POST، فلن يتم تخزينها مؤقتًا.

ما هي ذاكرة التخزين المؤقت للبيانات؟

ذاكرة التخزين المؤقت للبيانات هي ذاكرة تخزين مؤقت HTTP دائمة. اعتمادًا على منصتك، يمكن أن تتوسع الذاكرة المؤقتة تلقائيًا وتكون مشتركة عبر مناطق متعددة.

تعلم المزيد عن ذاكرة التخزين المؤقت للبيانات.

إعادة التحقق من البيانات

إعادة التحقق هي عملية مسح ذاكرة التخزين المؤقت للبيانات وإعادة جلب أحدث البيانات. هذا مفيد عندما تتغير بياناتك وتريد التأكد من عرض أحدث المعلومات.

يمكن إعادة التحقق من البيانات المخزنة مؤقتًا بطريقتين:

  • إعادة التحقق المبنية على الوقت: إعادة التحقق من البيانات تلقائيًا بعد مرور فترة زمنية معينة. هذا مفيد للبيانات التي تتغير بشكل غير متكرر ولا تكون الحداثة حرجة.
  • إعادة التحقق عند الطلب: إعادة التحقق من البيانات يدويًا بناءً على حدث (مثل إرسال نموذج). يمكن استخدام إعادة التحقق عند الطلب نهجًا يعتمد على العلامات أو المسارات لإعادة التحقق من مجموعات البيانات دفعة واحدة. هذا مفيد عندما تريد التأكد من عرض أحدث البيانات في أسرع وقت ممكن (مثل عند تحديث المحتوى من نظام إدارة المحتوى الخاص بك).

إعادة التحقق المبنية على الوقت

لإعادة التحقق من البيانات على فترات زمنية، يمكنك استخدام خيار next.revalidate في fetch لتعيين عمر ذاكرة التخزين المؤقت للمورد (بالثواني).

fetch('https://...', { next: { revalidate: 3600 } })

بدلاً من ذلك، لإعادة التحقق من جميع طلبات fetch في مقطع مسار، يمكنك استخدام خيارات تكوين المقطع.

layout.js | page.js
export const revalidate = 3600 // إعادة التحقق على الأكثر كل ساعة

إذا كان لديك عدة طلبات جلب في مسار معروض بشكل ثابت، ولكل منها تردد إعادة تحقق مختلف. سيتم استخدام أقل وقت لجميع الطلبات. بالنسبة للمسارات المعروضة ديناميكيًا، سيتم إعادة التحقق من كل طلب fetch بشكل مستقل.

تعلم المزيد عن إعادة التحقق المبنية على الوقت.

إعادة التحقق عند الطلب

يمكن إعادة التحقق من البيانات عند الطلب عن طريق المسار (revalidatePath) أو عن طريق علامة التخزين المؤقت (revalidateTag) داخل Route Handler أو Server Action.

لدى Next.js نظام وضع العلامات على الذاكرة المؤقتة لإبطال طلبات fetch عبر المسارات.

  1. عند استخدام fetch، لديك خيار وضع علامات على إدخالات الذاكرة المؤقتة بواحدة أو أكثر من العلامات.
  2. ثم يمكنك استدعاء revalidateTag لإعادة التحقق من جميع الإدخالات المرتبطة بهذه العلامة.

على سبيل المثال، يضيف طلب fetch التالي علامة التخزين المؤقت collection:

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

إذا كنت تستخدم Route Handler، يجب إنشاء رمز سري يعرفه فقط تطبيق Next.js الخاص بك. سيتم استخدام هذا الرمز لمنع محاولات إعادة التحقق غير المصرح بها. على سبيل المثال، يمكنك الوصول إلى المسار (إما يدويًا أو عبر webhook) باستخدام هيكل URL التالي:

URL
https://<your-site.com>/api/revalidate?tag=collection&secret=<token>
import { NextRequest } from 'next/server'
import { revalidateTag } from 'next/cache'

// مثال: webhook إلى `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 Response.json({ message: 'رمز سري غير صالح' }, { status: 401 })
  }

  if (!tag) {
    return Response.json({ message: 'معلمة العلامة مفقودة' }, { status: 400 })
  }

  revalidateTag(tag)

  return Response.json({ revalidated: true, now: Date.now() })
}
import { revalidateTag } from 'next/cache'

// مثال: webhook إلى `your-website.com/api/revalidate?tag=collection&secret=<token>`
export async function POST(request) {
  const secret = request.nextUrl.searchParams.get('secret')
  const tag = request.nextUrl.searchParams.get('tag')

  if (secret !== process.env.MY_SECRET_TOKEN) {
    return Response.json({ message: 'رمز سري غير صالح' }, { status: 401 })
  }

  if (!tag) {
    return Response.json({ message: 'معلمة العلامة مفقودة' }, { status: 400 })
  }

  revalidateTag(tag)

  return Response.json({ revalidated: true, now: Date.now() })
}

بدلاً من ذلك، يمكنك استخدام revalidatePath لإعادة التحقق من جميع البيانات المرتبطة بمسار.

import { NextRequest } from 'next/server'
import { revalidatePath } from 'next/cache'

export async function POST(request: NextRequest) {
  const path = request.nextUrl.searchParams.get('path')

  if (!path) {
    return Response.json({ message: 'معلمة المسار مفقودة' }, { status: 400 })
  }

  revalidatePath(path)

  return Response.json({ revalidated: true, now: Date.now() })
}
import { revalidatePath } from 'next/cache'

export async function POST(request) {
  const path = request.nextUrl.searchParams.get('path')

  if (!path) {
    return Response.json({ message: 'معلمة المسار مفقودة' }, { status: 400 })
  }

  revalidatePath(path)

  return Response.json({ revalidated: true, now: Date.now() })
}

تعلم المزيد عن إعادة التحقق عند الطلب.

معالجة الأخطاء وإعادة التحقق

إذا حدث خطأ أثناء محاولة إعادة التحقق من البيانات، سيتم الاستمرار في تقديم آخر بيانات تم إنشاؤها بنجاح من الذاكرة المؤقتة. في الطلب التالي، سيقوم Next.js بإعادة محاولة إعادة التحقق من البيانات.

عدم استخدام التخزين المؤقت للبيانات

لا يتم تخزين طلبات fetch مؤقتًا إذا:

  • تمت إضافة cache: 'no-store' إلى طلبات fetch.
  • تمت إضافة خيار revalidate: 0 إلى طلبات fetch الفردية.
  • كان طلب fetch داخل Route Handler يستخدم طريقة POST.
  • جاء طلب fetch بعد استخدام headers أو cookies.
  • تم استخدام خيار مقطع المسار const dynamic = 'force-dynamic'.
  • تم تكوين خيار مقطع المسار fetchCache لتخطي التخزين المؤقت افتراضيًا.
  • استخدم طلب fetch رؤوس Authorization أو Cookie وكان هناك طلب غير مخزن مؤقتًا فوقه في شجرة المكونات.

طلبات fetch الفردية

لعدم استخدام التخزين المؤقت لطلبات fetch الفردية، يمكنك تعيين خيار cache في fetch إلى 'no-store'. سيؤدي هذا إلى جلب البيانات ديناميكيًا في كل طلب.

layout.js | page.js
fetch('https://...', { cache: 'no-store' })

عرض جميع خيارات cache المتاحة في مرجع واجهة برمجة تطبيقات fetch.

طلبات fetch المتعددة

إذا كان لديك عدة طلبات fetch في مقطع مسار (مثل Layout أو Page)، يمكنك تكوين سلوك التخزين المؤقت لجميع طلبات البيانات في المقطع باستخدام خيارات تكوين المقطع.

على سبيل المثال، استخدام const dynamic = 'force-dynamic' سيؤدي إلى جلب جميع البيانات في وقت الطلب، وعرض المقطع ديناميكيًا.

layout.js | page.js
// أضف
export const dynamic = 'force-dynamic'

هناك قائمة واسعة من خيارات تكوين المقطع، مما يمنحك تحكمًا دقيقًا في السلوك الثابت والديناميكي لمقطع مسار. راجع مرجع API للمزيد.

جلب البيانات على الخادم باستخدام مكتبات الطرف الثالث

في الحالات التي تستخدم فيها مكتبة طرف ثالث لا تدعم أو تعرض fetch (مثل قاعدة بيانات، نظام إدارة محتوى، أو عميل ORM)، يمكنك تكوين سلوك التخزين المؤقت وإعادة التحقق من تلك الطلبات باستخدام خيار تكوين مقطع المسار ودالة cache في React.

سيعتمد ما إذا كانت البيانات مخزنة مؤقتًا أم لا على ما إذا كان مقطع المسار معروضًا بشكل ثابت أو ديناميكي. إذا كان المقطع ثابتًا (افتراضيًا)، سيتم تخزين ناتج الطلب وإعادة التحقق منه كجزء من مقطع المسار. إذا كان المقطع ديناميكيًا، لن يتم تخزين ناتج الطلب وسيتم إعادة جلبه في كل طلب عند عرض المقطع.

معلومة مفيدة:

يعمل Next.js على واجهة برمجة تطبيقات، unstable_cache، لتكوين سلوك التخزين المؤقت وإعادة التحقق من طلبات الطرف الثالث الفردية.

مثال

في المثال التالي:

  • تم تعيين خيار revalidate إلى 3600، مما يعني أنه سيتم تخزين البيانات مؤقتًا وإعادة التحقق منها على الأكثر كل ساعة.
  • يتم استخدام دالة cache في React لتذكر طلبات البيانات.
import { cache } from 'react'

export const revalidate = 3600 // إعادة التحقق من البيانات على الأكثر كل ساعة

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

export const revalidate = 3600 // إعادة التحقق من البيانات على الأكثر كل ساعة

export const getItem = cache(async (id) => {
  const item = await db.item.findUnique({ id })
  return item
})

على الرغم من استدعاء دالة getItem مرتين، سيتم إجراء استعلام واحد فقط إلى قاعدة البيانات.

import { getItem } from '@/utils/get-item'

export default async function Layout({
  params: { id },
}: {
  params: { id: string }
}) {
  const item = await getItem(id)
  // ...
}
import { getItem } from '@/utils/get-item'

export default async function Layout({ params: { id } }) {
  const item = await getItem(id)
  // ...
}
import { getItem } from '@/utils/get-item'

export default async function Page({
  params: { id },
}: {
  params: { id: string }
}) {
  const item = await getItem(id)
  // ...
}
import { getItem } from '@/utils/get-item'

export default async function Page({ params: { id } }) {
  const item = await getItem(id)
  // ...
}

جلب البيانات على العميل باستخدام Route Handlers

إذا كنت بحاجة إلى جلب البيانات في مكون عميل، يمكنك استدعاء Route Handler من العميل. يتم تنفيذ Route Handlers على الخادم وإرجاع البيانات إلى العميل. هذا مفيد عندما لا تريد الكشف عن معلومات حساسة للعميل، مثل رموز API.

راجع وثائق Route Handler للحصول على أمثلة.

مكونات الخادم و Route Handlers

نظرًا لأن مكونات الخادم تعرض على الخادم، لا تحتاج إلى استدعاء Route Handler من مكون خادم لجلب البيانات. بدلاً من ذلك، يمكنك جلب البيانات مباشرة داخل مكون الخادم.

جلب البيانات على العميل باستخدام مكتبات الطرف الثالث

يمكنك أيضًا جلب البيانات على العميل باستخدام مكتبات طرف ثالث مثل SWR أو React Query. توفر هذه المكتبات واجهات برمجة تطبيقات خاصة بها لتذكر الطلبات، التخزين المؤقت، إعادة التحقق، وتحوير البيانات.

واجهات برمجة التطبيقات المستقبلية:

use هي دالة React تقبل وتتعامل مع وعد تم إرجاعه بواسطة دالة. لف fetch في use غير موصى به حاليًا في مكونات العميل وقد يؤدي إلى إعادة عرض متعددة. تعلم المزيد عن use في RFC React.