Open Fredkiss3 opened 1 year ago
this is stupid (sorry for my harsh language).
I have this use case : i use turso
with drizzle
which uses @libsql/client
in the hood, each request is a POST
with an Authorization
header. I don't have the means to change the code under the hood but each request returns a 200 OK
response. Now how can i make sure these requests are never cached as they are for my database and i want each request to give me the latest data ? With this i loose control over how i can fix the issue.
If you want a real reproduction link you can see here : https://nextjs-13-contacts.vercel.app/contacts/51 (link to the source code) , in this app if you click on the star button, it will work the first time, but not again, while it is supposed to toggle the data (the favorite
column should be toggled from true
to false
and vice-versa), since it caches the data, only the first click will update, and if the data is already cached, clicking on the button won't do anything. The route is already using dynamic rendering.
But for what i read from the docs, using an Authorization
header should also skip the cache, which is an expected behavior as usually you want you don't want a token to be valid for longer that it needs :
Requests are not cached if:
- [...] the fetch is a POST request (or uses Authorization or cookie headers)
I thought that means "read headers" instead of passing it.
I don't sure how is it originally expected to be (it can be either a internal bug or a docs issue) but Next.js doesn't skip cache even if you passed an authorisation header.
As docs mentioned, you can opt out of fetch caching by configuring options on individual fetch
(Which is 100% in control), or using segment-level caching.
For me it segment config does not work, because I use parallel routes ?
And I tried to set fetchCache
to the root layout, it doesn't work.
You wouldn't read headers
to Authenticate to your API server or database, unless you want to implement a proxy server in next the header would need to be passed from the client to your fetch request.
You would use a secret, in a environment variable.
Keep in ming that when i run requests that create data it gets cached (it does not create a new entry to the database), i had to add a createdAt
field here to bust the cache entry created :
const [res] = await db
.insert(contacts)
.values({
favorite: false,
createdAt: new Date(),
})
.returning({ insertedId: contacts.id })
.all();
I have made some researches on the source code, you can see here.
In your case, you need to configure the fetch
options. Otherwise, you should use Route Handlers instead, requests in a POST
route handler won't be cached.
I cannot configure fetch options as i use a wrapper library, and i don't want to use route handlers, (i use server actions).
Reading the comment from the source code, it seems like POST
request & other methods are not cached only when a dynamic function is used before (cookies()
or headers()
) or revalidate
is set to 0
which mark the page as dynamic :
// if there are authorized headers or a POST method and
// dynamic data usage was present above the tree we bail
// e.g. if cookies() is used before an authed/POST fetch
I've also tested this, and unless you call cookies
or headers
before making the request, the POST
request is not cached, else it will always cache the request.
this is not good as DELETE
and PUT
requests should never be cached at all. i may understand POST
for graphql but not the other ones.
Segment-level caching doesn't work for server actions, I also experienced the same problem in my project. My approach is to use Route Handler instead (with SWR)
As this behaviour is clearly documented, it might be better to open a Feature request or something else (it is not a bug).
In fact, I've opened a feature request for that months ago but didn't get any attention.
right now, i manually call cookies()
function everytime before making a DB request, but it shouldn't be.
I'm gonna guess that since it has these variable names hasUnCacheableHeader
& isUnCacheableMethod
means that POST
& other methods (PUT
, DELETE
, OPTIONS
) should not be cached at all and/or the if request has an Authorization
headers, it should be the same.
What is also funny is that if it was supposed to cache, it would also cache POST
requests returning any status code, but here if the fetch returns any code other than 200
, it won't cache it.
right now, i manually call
cookies()
function everytime before making a DB request, but it shouldn't be.I'm gonna guess that since it has these variable names
hasUnCacheableHeader
&isUnCacheableMethod
means thatPOST
& other methods (PUT
,DELETE
,OPTIONS
) should not be cached at all and/or the if request has anAuthorization
headers, it should be the same.What is also funny is that if it was supposed to cache, it would also cache
POST
requests returning any status code, but here if the fetch returns any code other than200
, it won't cache it.
Because if it's not 200, it means the fetch has failed. Why do they need to cache failed requests?
Also, this behaviour is expected, please refer to the docs: Post
requests are not cached if a dynamic method is used.
This has been answered in the Caching section on the documentation.
there are other status codes like 201
& 3xx
which still indicates successful requests.
From MDN docs : https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#successful_responses
Verify canary release
Provide environment information
Which area(s) of Next.js are affected? (leave empty if unsure)
App Router, Data fetching (gS(S)P, getInitialProps), Middleware / Edge (API routes, runtime)
Link to the code that reproduces this issue or a replay of the bug
https://github.com/Fredkiss3/next-fetch-post-cache-edge-bug
To Reproduce
/
in your browser and refresh the browser at least twicePOST /api/cache
do not change after reloads{ cache: ... }
will always show the same value.next/cache/fetch-cache/
you will see an entry for the cached dataDescribe the Bug
When using edge runtime POST requests are cached if the response returns a
200 OK
status code, even if the request contains anAuthorization
header. This issue do not happen if the request returns a different status code (like201
for ex).Expected Behavior
I may understand why this behavior has been introduced (maybe to help people cache graphql queries), but using an
Authorization
header should opt the fetch out of caching, as it is said in the docs :I also thought that POST requests would not automatically be cached but there is not a clear message in the docs as to wether POST are also cached or are not cached by default. I don't know what the desired behavior is supposed to be, but if you want the user to cache their POST request, they can still manually add a
cache
field to the fetch i think ?This issue is particularly annoying as it is difficult to spot if you are using a data-fetching or database library (in my case
turso
with the HTTP client), most of these librairies usePOST
requests and return 200 OK as statuses. I think it could cause the same frustration if used with appolo.Which browser are you using? (if relevant)
No response
How are you deploying your application? (if relevant)
No response