कैशिंग और रीवैलिडेशन
रूट सेगमेंट कॉन्फ़िग, unstable_cache, revalidateTag / revalidatePath और stale-while-revalidate।
रूट हैंडलर के रिस्पॉन्स और डेटा कैश करें, फिर टाइमर पर या मांग पर उन्हें दोबारा वैलिडेट करें। बैकग्राउंड में एक रिफ़्रेश चलने के दौरान पुरानी एंट्री तुरंत दी जाती हैं।
रूट हैंडलर
GET रूट हैंडलर डिफ़ॉल्ट रूप से डायनेमिक होते हैं। कैशिंग चालू करने के लिए रूट सेगमेंट कॉन्फ़िग का इस्तेमाल करें:
export const revalidate = 60;export async function GET() { const posts = await db.posts.latest(); return Response.json(posts);}| एक्सपोर्ट | व्यवहार |
|---|---|
| (कोई नहीं) | हर रिक्वेस्ट पर चलता है |
dynamic = "force-static" | दोबारा वैलिडेट होने तक कैश रहता है |
revalidate = N | कैश रहता है; N सेकंड बाद पुराना हो जाता है |
revalidate = 0 या dynamic = "force-dynamic" | हर रिक्वेस्ट पर चलता है |
अन्य dynamic वैल्यू TX031 के साथ विफल होती हैं। इसमें Next.js की "error" वैल्यू भी शामिल है, क्योंकि Text.js पता नहीं लगा सकता कि कोई हैंडलर कब रिक्वेस्ट पढ़ता है।
कैश किए गए हैंडलर को हर पाथनेम के लिए एक एंट्री मिलती है, और उनका request बिल्ड के समय जैसा होता है: उसमें क्वेरी स्ट्रिंग, हेडर, कुकी या बॉडी नहीं होती। इन्हें डायनेमिक हैंडलर में पढ़ें। सिर्फ़ GET और HEAD कैश होते हैं। 5xx स्टेटस, Set-Cookie हेडर या Cache-Control: private / no-store वाले रिस्पॉन्स लौटाए जाते हैं, लेकिन कभी स्टोर नहीं होते।
हेडर
कैश किए गए रिस्पॉन्स में ये हेडर होते हैं:
| हेडर | वैल्यू |
|---|---|
x-textjs-cache | MISS, HIT या STALE |
Cache-Control | s-maxage=N, stale-while-revalidate=<1 year − N>, या force-static के लिए s-maxage=31536000 |
जब मिडलवेयर रिक्वेस्ट के लिए कुकी सेट करता है, रीराइट करता है या रिक्वेस्ट हेडर ओवरराइड करता है, तो जनरेट किया गया हेडर private, no-cache, no-store, max-age=0, must-revalidate हो जाता है, ताकि शेयर किए गए कैश किसी विज़िटर के लिए लिया गया फ़ैसला दोबारा लागू न करें। हैंडलर का सेट किया हुआ Cache-Control हेडर बना रहता है।
text dev कमांड में सेगमेंट कॉन्फ़िग लागू होता है लेकिन कुछ भी स्टोर नहीं होता, इसलिए हर रिक्वेस्ट पर हैंडलर चलता है।
डेटा कैश
@textjs/app/cache के unstable_cache से किसी भी एसिंक फ़ंक्शन को कैश करें:
import { unstable_cache } from "@textjs/app/cache";export const getPosts = unstable_cache( async (author: string) => db.posts.byAuthor(author), ["posts"], { tags: ["posts"], revalidate: 300 },);की में की के हिस्से और आर्ग्युमेंट शामिल होते हैं, जिन्हें वैल्यू के आधार पर एन्कोड किया जाता है (undefined और null, Map, Set, Date और URL आर्ग्युमेंट अलग-अलग रहते हैं)। की के हिस्सों के बिना, फ़ंक्शन का सोर्स और वह जगह जहां उसे बनाया गया था, उसकी पहचान करते हैं; जब भी फ़ंक्शन वैल्यू को क्लोज़र में रखता है, की के हिस्से पास करें, उदाहरण के लिए जब उसे हर रिक्वेस्ट पर या लूप में बनाया जाता है। नतीजे JSON में सीरियलाइज़ होते हैं, इसलिए हर कॉल एक नई कॉपी लौटाती है (और Date ऑब्जेक्ट स्ट्रिंग के रूप में लौटते हैं); सीरियलाइज़ न हो सकने वाला नतीजा एरर थ्रो करता है। cache(fn, { key, tags, revalidate }) वही सेटिंग एक ऑप्शन ऑब्जेक्ट के रूप में लेता है।
कैश किए गए रूट हैंडलर के अंदर, डेटा एंट्री अपने टैग और अपना सबसे छोटा revalidate रूट तक भेजती हैं: "posts" को दोबारा वैलिडेट करने से /api/posts भी रिफ़्रेश होता है। revalidate: 0 वाली डेटा कॉल रूट को डायनेमिक बना देती है।
दोबारा वैलिडेट करते समय पुराना डेटा देना
जब कोई एंट्री revalidate से ज़्यादा पुरानी हो जाती है:
- अगली रिक्वेस्ट को पुरानी वैल्यू तुरंत मिलती है (
STALE)। - बैकग्राउंड में एक रिफ़्रेश चलता है; एक साथ आने वाली रिक्वेस्ट उसे शेयर करती हैं।
- अगर रिफ़्रेश एरर थ्रो करता है, तो पुरानी वैल्यू मिलती रहती है और एरर लॉग होता है।
Vercel और Cloudflare पर, बैकग्राउंड रिफ़्रेश waitUntil() के साथ रजिस्टर होते हैं। एक साल से पुरानी एंट्री की गणना हमेशा फ़ोरग्राउंड में दोबारा होती है।
मांग पर दोबारा वैलिडेट करना
import { revalidatePath, revalidateTag } from "@textjs/app/cache";export async function POST(request: Request) { // Anyone can POST here: require a secret shared with your CMS or webhook. if (request.headers.get("authorization") !== `Bearer ${process.env.REVALIDATE_SECRET}`) { return new Response("Unauthorized", { status: 401 }); } await revalidateTag("posts"); await revalidatePath("/api/feed"); return Response.json({ revalidated: true });}| कॉल | अमान्य होने वाला डेटा |
|---|---|
revalidateTag("posts") | posts टैग वाला सारा डेटा; अगली रिक्वेस्ट पर दोबारा गणना |
revalidateTag("posts", "max") | पुराने के रूप में चिह्नित डेटा; रिफ़्रेश के दौरान अगली रिक्वेस्ट को पुराना डेटा |
revalidatePath("/api/feed") | वह पाथ |
revalidatePath("/api/posts/[id]") | डायनेमिक रूट का हर पाथ |
revalidatePath("/api", "layout") | /api के तहत सब कुछ |
दोनों एक प्रॉमिस लौटाते हैं। इन-मेमोरी स्टोर में डेटा तुरंत अमान्य हो जाता है; जब कस्टम स्टोर एसिंक्रोनस हो, तो इसके लिए await का इस्तेमाल करें।
कस्टम कैश स्टोर
हर सर्वर इंस्टेंस का डिफ़ॉल्ट स्टोर एक इन-मेमोरी LRU (1,000 एंट्री, लगभग 50 MB) होता है। इंस्टेंस के बीच कैश शेयर करने के लिए, cacheHandler को ऐसे मॉड्यूल पर सेट करें जो CacheStore को डिफ़ॉल्ट एक्सपोर्ट करता हो:
export default defineConfig({ cacheHandler: "./cache-handler.ts" });import type { CacheStore } from "@textjs/app/cache";const store: CacheStore = { async get(key) { return redis.get(key).then((v) => (v ? JSON.parse(v) : undefined)); }, async set(key, entry) { await redis.set(key, JSON.stringify(entry)); }, async delete(key) { await redis.del(key); }, async markTags(tags, mode, at) { for (const t of tags) await redis.hset(`tag:${t}`, mode, at); }, async getTagState(tags) { /* max expiredAt / staleAt across tags */ return { expiredAt: 0, staleAt: 0 }; },};export default store;एंट्री सादा JSON होती हैं। setCacheStore(store) इसके बजाय रनटाइम पर स्टोर इंस्टॉल करता है।
प्लैटफ़ॉर्म से जुड़ी जानकारी
- हर सर्वरलेस इंस्टेंस (Vercel फ़ंक्शन, Cloudflare आइसोलेट) का अपना इन-मेमोरी कैश होता है।
Cache-Control: s-maxageसे प्लैटफ़ॉर्म CDN भी रिस्पॉन्स कैश कर सकता है; मांग पर दोबारा वैलिडेट करने से CDN का कैश साफ़ नहीं होता। जबrevalidateTagका हर इंस्टेंस तक पहुंचना ज़रूरी हो, तो शेयर किया गयाcacheHandlerइस्तेमाल करें। - Cloudflare के लिए
nodejs_compatफ़्लैग ज़रूरी है (कैशAsyncLocalStorageका इस्तेमाल करता है)। डिप्लॉय करना देखें।