Next.js Discord

Discord Forum

Cache api requests server side

Answered
Xander posted this in #help-forum
Open in Discord
On client side I'd use something like vercel SWR to handle caching and invalidation after a certain period, but how would I achieve this on the server?

All my external api stuff is done on the server partly for security reasons with a httponly cookie but I don't want to be pegging my api with requests like /user/me to determine the contents of my navbar for example every single time someone navigates
Answered by B33fb0n3
on serverside you can use the data cache to cache specific parts of data. You can use it for example with fetch requests, or if you need to use external libraries and want to cache that results, use unstable_cache
View full answer

92 Replies

or for example one part of my app I need to fetch something, and in a completely other part of the app I need to same thing, how can i avoid double requests?
Answer
sure thing. Happy to help
@B33fb0n3 Question, is data cache available with route handlers? Just found out it can except on POST. What if an external api is called inside a route handler with fetch function of course? Will it still be cached?
@zhefciad <@301376057326567425> Question, is data cache available with route handlers? Just found out it can except on POST. What if an external api is called inside a route handler with fetch function of course? Will it still be cached?
If you call your function inside a post route handler, you should be able to still use your data cache without your fetch or unstable_cache functions
@B33fb0n3 If you call your function inside a post route handler, you should be able to still use your data cache without your fetch or unstable_cache functions
does this data cache work in dev env? my test api seems to be getting hit more than once per render
@Xander does this data cache work in dev env? my test api seems to be getting hit more than once per render
unstable cache cache it also in dev mode. I am unsure about fetch
@Xander does that solves your problem?
@B33fb0n3 <@320596098689400833> does that solves your problem?
i dont know yet i haven't been working on the frontend
@Xander ?
@B33fb0n3 <@320596098689400833> ?
I will let you know when I have tested it
@B33fb0n3 <@320596098689400833> ?
how did you say to cache between page loads?
@Xander how did you say to cache between page loads?
what specifically do you mean?
for example my navbar queries an api to get the items, but I want to cache this between page loads
@Xander for example my navbar queries an api to get the items, but I want to cache this between page loads
yea, than you can use the data cache to cache specific parts of data. You can use it for example with fetch requests, or if you need to use external libraries and want to cache that results, use unstable_cache
wdym use external libraries
external libraries are for example if you using aws amplify graphql to fetch your data (external because AWS GraphQL is not inside your app integrated)
so if the fetch calls an external service rather than the next app
if you can use fetch, the result will be also cached automatically iirc
/user/me and /tag/list and /sound/1/search and /library/list are all being called every time I reload the page
that does not sound right
@B33fb0n3 that does not sound right
these aren't next api routes, they're to an external api
uhm ok. Is this about the same topic? I am kinda confused right now
yes it's the same topic.
I have server components that fetch from apis, like fetch("localhost:8080/library/list"), an external api that i have created not in nextjs.
then when I reload the page, I wish for the fetch to be cached
Ah ok. And you are using „fetch“ method for them, right?
@Xander yes
then it should be cached automatically in server component:
English Lop
@Xander What version of next are you using?
Because in 14 they are cached by default but in 15 that's not the case!
The latest 14
English Lop
what is the revalidation time you've set?
@English Lop what is the revalidation time you've set?
I guess he does not set any
@B33fb0n3 I guess he does not set any
English Lop
That might be it!
@English Lop That might be it!
why? Then it should be cached an unlimited amount of time
@B33fb0n3 why? Then it should be *cached* an unlimited amount of time
English Lop
Sorry, my bad! revalidate: 0 will stop fetch requests from caching
@B33fb0n3 but he want it to be cached xD
English Lop
That' why I'm asking him about the revalidation time
@English Lop That' why I'm asking him about the revalidation time
wait it said server actions don't get cached right?
is any function in a file with 'use server' a server action?
oh well i removed the directives and it still doesn't get cached sooo
@Xander oh well i removed the directives and it still doesn't get cached sooo
you might want to share some code, to help you better
i'll do my best to give you snippets but it's a private commissioned project
alright so I have the navbar component
export default async function NavBar() {
  const loggedIn = cookies().get('session') != undefined

  return loggedIn ? <LoggedInLinks /> : <LoggedOutLinks />
}

async function LoggedInLinks() {
  const isManager = (await fetchSelfUser()).successful?.role.manager || false

  ...
}
there are no use directives in the navbar file
then fetchselfuser is in a file where it is the only function, no directives
export async function fetchSelfUser() {
    return await apiFetch<User>(`/user/me`)
}
then my api fetch is in yet another file
export async function apiFetch<T>(path: string, auth: boolean = true, init?: RequestInit) {
  //....
  await fetch(bla bla bla)
}
also no use directives
yet every time I reload the page, /user/me is requested to the api
@Xander alright so I have the navbar component tsx export default async function NavBar() { const loggedIn = cookies().get('session') != undefined return loggedIn ? <LoggedInLinks /> : <LoggedOutLinks /> } async function LoggedInLinks() { const isManager = (await fetchSelfUser()).successful?.role.manager || false ... }
thanks for the code.
- NavBar is a client or server component?
- Do you have anything else inside the fetch(bla bla bla) or just fetch("https://someurl.com/api/endpoint/")?
- Would you mind sharing the request inside your network tab (see attached)
i've previously attached chrome devtools to it but the network tab was missing
export async function apiFetch<T>(path: string, auth: boolean = true, init?: RequestInit): Promise<FetchResponse<T>> {
    if (auth) {
        const session = await getSessionCookie()
        if (!session) {
            return Promise.resolve({ error: 'Not logged in' })
        }

        init = {
            ...init,
            headers: {
                "Authorization": `Bearer ${session}`,
                ...init?.headers
            },
        }
    }
    const result = await fetch(`${API_BASE_URL}${path}`, init)
    const json = await result.json() as ApiResponse<T>
    
    if (json.success) {
        return {
            successful: json.data,
        }
    } else {
        return {
            error: json.message,
        }
    }
}
how do you know, that the result is not the cached result?
@B33fb0n3 how do you know, that the result is not the cached result?
because i am watching the api logs being spammed when i reload the page
the api is made by me and hosted locally on the machine, its just not in next it's a different framework
alright. Can you add force-cache to it?
Like:
await fetch(`https://...`, { cache: 'force-cache' })
that indeed works
so why does it need to be forced?
i thought it was an automatic thing
English Lop
Yes, even I'm boggled here!
@Xander i thought it was an automatic thing
I thought so too. I guess it's maybe because of the auth header. But that's just a guess
with force-cache can i still specify the cache lifetime?
so it expires after x minutes
yes you can use this:
fetch(`https://...`, { next: { revalidate: false | 0 | number } })

That sets the cache lifetime of a resource (in seconds).

- false - Cache the resource indefinitely. Semantically equivalent to revalidate: Infinity. The HTTP cache may evict older resources over time.
- 0 - Prevent the resource from being cached.
- number - (in seconds) Specify the resource should have a cache lifetime of at most n seconds.
i use cookies for auth of course
ahhh that's the reason. Yea, don't cache results, that belongs to specific users or tokens
the cache key includes all headers does it not?
including authorization
so it doesn't leak data
the fetch will be called serverside. The data cache is only available serverside. So even when they are included, nothing will leak
@B33fb0n3 the fetch will be called serverside. The data cache is only available serverside. So even when they are included, nothing will leak
okay, so what's the problem in caching data belonging to specific tokens?
@Xander okay, so what's the problem in caching data belonging to specific tokens?
Imagine you revoke a specific token and the result of a specific fetch operation is cached, then the user can still access this data, even if the token is invalid
e.g. a webhook to the webserver
or just an api route i guess
you can easily invalidate your data cache, yes
oh cool tags
btw, that what I meant here: https://nextjs-forum.com/post/1253831819460345859#message-1257652597641318441

Is actually true: (see attached)
@Xander oh cool tags
yea pretty nice thing
what does it mean
and there's an uncached request above it in the component tree
@B33fb0n3 yea pretty nice thing
if I invalidate a cache, is that invalidates for all users?
@Xander if I invalidate a cache, is that invalidates for *all* users?
yes, the data cache is a serverside cache. So it's the same for all users