Data not updating when deployed Nextjs13 app on Vercel, despite using cache: 'no-store' in fetch request
Asked Answered
S

5

7

I have a feed component in my application that fetches data from an API endpoint. The component works fine when I test it on my local build, but when I deploy it on Vercel, it doesn't fetch the latest data. I suspect this issue is related to caching. To address the problem, I added the cache: 'no-store' option to the fetch request, but it doesn't seem to solve the problem. I would appreciate any help or suggestions to resolve this issue.

"use client";

  const fetchPosts = async () => {
    const response = await fetch("/api/prompt", {
      cache: 'no-store',
    });
    const data = await response.json();
    setAllPosts(data);
  };

  useEffect(() => {
    fetchPosts();
  }, []);

GitHub Link: https://github.com/justinwkUKM/promptify/blob/main/components/Feed.jsx

Note: Please provide any suggestions or solutions for the caching issue when deploying on Vercel. Thank you!

Sokoto answered 29/5, 2023 at 11:7 Comment(0)
S
14

For anyone who is still encountering this issue when deploying to Vercel:

The issue is being caused by the file being rendered statically by Next.JS. According to the documentation: By default, Next.js statically renders routes to improve performance.

You can see this in the output printed when npm run build is executed. A simple example of the output is shown below.

Route (app)
┌ ○ /api/prompt
└ λ /api/prompt/[id]

λ  (Server)  server-side renders at runtime (uses getInitialProps or getServerSideProps)
○  (Static)  automatically rendered as static HTML (uses no initial props)

In order to prevent Next.JS from statically rendering this site it needs some way to know its dynamic. According to the documentation: if a dynamic function or a dynamic fetch() request (no caching) is discovered, Next.js will switch to dynamically rendering the whole route at request time.

The static and dynamic behavior of a route can be controlled using the Route Segment Config features of Next.JS

Specifically you can export the dynamic variable with the value of 'force-dynamic' as shown

import Prompt from "@models/prompt";
import { connectToDB } from "@utils/database";

export const dynamic = 'force-dynamic';
export const GET = async (request) => {
  try {
    await connectToDB();

    const prompts = await Prompt.find({}).populate('creator');

    return new Response(JSON.stringify(prompts), { status: 200 });
  } catch (error) {
    return new Response("Failed to fetch all prompts", { status: 500 });
  }
} 

This export ensures that Next.JS renders the route dynamically!

A little side note: This is functionally similar to adding export const revalidate = 0; according to the docs.

Siloum answered 24/7, 2023 at 3:6 Comment(1)
Just wanted to say thanks for this. Had my call in a component that the page called and added the revalidate const to there and that worked in local but not vercel and you mentioning how its the pages that are cached made me think to add it to the actual page itself and that worked fine. Saved me some head banging!Veriee
H
5

You have literally the exact same issue as me. I was able to find a fix with this:

/app/api/prompt/route.js

import Prompt from "@models/prompt";
import { connectToDB } from "@utils/database";

export const revalidate = 1; //revalidate api every 1 second
export const GET = async (request) => {
    try {
        await connectToDB()

        const prompts = await Prompt.find({}).populate('creator')

        return new Response(JSON.stringify(prompts), { status: 200 })
    } catch (error) {
        return new Response("Failed to fetch all prompts", { status: 500 })
    }
} 

I can't really explain why Next works this way, but this fixed it for me.

Hardihood answered 24/6, 2023 at 21:8 Comment(2)
It works to me, thanks. In my case, my api route became static (not ssg), so that's why data were not updated. I have used revalidate = 0 without double refreshing the browser.Apply
Thank you so much! I deployed my interview task and saw this. This might just help me clear it!Manhunt
N
1

I encountered a similar issue when deploying my app on Vercel. However, I was able to resolve it by updating the fetchPosts function to include the following changes:

const response = await fetch("/api/prompt", { next: { revalidate: 1 } });

Additionally, I made updates to the app/api/prompt/route.js file

import Prompt from "@models/prompt";
import { connectToDatabase } from "@utils/database";
import { NextRequest, NextResponse } from "next/server";
import { revalidatePath } from "next/cache";

export const GET = async (request) => {
  try {
    await connectToDatabase();

    const prompts = await Prompt.find({}).populate("creator");

    //To dynamically get the path
    const path = request.nextUrl.searchParams.get("path") || "/";

    revalidatePath(path);

    return NextResponse.json(prompts);
  } catch (error) {
    return new Response("Failed to fetch all prompts", { status: 500 });
  }
};

For a detailed explanation, you can refer to this YouTube video: https://www.youtube.com/watch?v=TZXMT-EG7Ak&t=98s

I hope that this helps you in resolving the issue.

Nanji answered 29/6, 2023 at 12:20 Comment(0)
I
1

I got the same problem before just use dynamic :

export const dynamic = 'force-dynamic';


import Prompt from "@models/prompt";
    import { connectToDB } from "@utils/database";
    
    export const dynamic = 'force-dynamic';
    export const GET = async (request) => {
      try {
        await connectToDB();
    
        const prompts = await Prompt.find({}).populate('creator');
    
        return new Response(JSON.stringify(prompts), { status: 200 });
      } catch (error) {
        return new Response("Failed to fetch all prompts", { status: 500 });
      }
    } 
Inkerman answered 24/2 at 22:15 Comment(0)
S
1

I ran into the same problem when deploying my project to Vercel. Apparently, Vercel automatically sets your route handlers to static which causes outdated data, or values not being updated at all until the next build when fetching values from routes.

I was able to fix mine by adding this one line of code in my route.ts page.

//this line will avoid caching when deployed to vercel
//value options
// false | 0 | number
export const revalidate = 0;

This line of code will basically fetch new values every time the route is called. Setting the value to 0 will remove the default caching by Vercel.

Here's the entire route.ts code for reference:

import { NextResponse } from "next/server";
import OpenAI from "openai";
import { ChatCompletionMessageParam } from "openai/resources/index.mjs";

//this line will avoid caching when deployed to vercel
//value options
// false | 0 | number
export const revalidate = 0;

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

const instructionMessage: ChatCompletionMessageParam = {
  role: "system",
  content: `
        You will give me trivia questions in JSON format. Your response should not include anything aside from the JSON format. The interface below is the format of your JSON response.

        Notes:
        -The number of questions will depend on my next prompt.
        -For each question, you will need to give me 4 options
        -No need to add new lines to your responses
        -Make the trivia questions interesting and not too generic
        

        export interface QuestionInterface {
            questions: {
                id: number;
                question: string;
                options: string[];
                correctAnswer: string;
            }[];
        }
        `,
};

export async function GET(req: any) {
  try {
    const numOfQuestions: OpenAI.Chat.ChatCompletionMessageParam = {
      role: "user",
      content: "10",
    };

    if (!openai.apiKey) {
      return new NextResponse("OpenAI API Key not configured", { status: 500 });
    }

    const response = await openai.chat.completions.create({
      model: "gpt-4o-2024-05-13",
      messages: [instructionMessage, numOfQuestions],
    });

    // console.log(response.choices[0].message);
    // const triviaQuestions = response.choices[0].message.content
    //   ? JSON.parse(response.choices[0].message.content)
    //   : "";

    const triviaQuestions = response.choices[0].message.content
      ? JSON.parse(response.choices[0].message.content)
      : "";

    // return NextResponse.json(triviaQuestions);

    // Set headers to disable caching
    const headers = {
      "Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate",
      Pragma: "no-cache",
      Expires: "0",
    };

    // Return the JSON response with headers
    return NextResponse.json(triviaQuestions, { headers });
  } catch (error) {
    console.log("[Retrieve Trivia Questions Error]", error);
    return new NextResponse(`Internal Error: ${error}`, { status: 500 });
  }
}

For more info about Route handlers being static by default

Sibilant answered 17/6 at 0:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.