Fraser Boag

How to stop an API endpoint caching with Next.js app router

Back to all notes
Dev
3rd August '23

Note: The content below relates specifically to a project using Next.js 13's new app router. If you're using the older pages router, the information below probably isn't going to be that helpful.


I recently set up a simple cron to write a log to Supabase (the remote DB I use for this blog) once per day to get round an irritating feature of Supabase's free tier which means your account is paused every couple of weeks if there's no database or API activity. Since this blog is mostly statically generated and I don't update it all that often, this can be a pretty frequent occurrence.

Here's that code for reference:

import { NextResponse } from 'next/server'
import { createClient } from '@supabase/supabase-js'

export async function GET() {
  const supabase = createClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
    { auth: { persistSession: false } }
  )

  const { data, error } = await supabase
    .from('logs')
    .insert([{ created_at: new Date() }])
    .select()

  return NextResponse.json({ data: data, error: error })
}

Unfortunately, Next.js/Vercel in its desperation to cache absolutely everything (generally a good thing, don’t get me wrong) aggressively caches the response even from serverless functions which perform an action. This means that my cron would hit the endpoint once per day, but the actual function would rarely run - simply returning the cached response from the last time it did run successfully. Instead I want all of the above code to run every single time this endpoint is hit.

If you're using JavaScript fetch this is pretty easily resolved in the options of your function call, which is well documented here. What isn't so well documented is what to do if you're not using fetch, for example using something like the Supabase client like I am.

The answer, fortunately, is simple - export a variable at the top level of your file called revalidate, set to a time in seconds that you're happy for your endpoint to expire. For me this looked like this:

export const revalidate = 0

And that's all there is to it - my script now runs every time the endpoint is hit and the response is never cached.

More notesAll notes

Get in touch  —  fraser.boag@gmail.com
Copyright © Fraser Boag 2013 - 2024. All rights reserved.