Server Component Data Fetching on each request
Unanswered
sudo posted this in #help-forum
sudoOP
Hello,
I'm in the app directory, and i have a page for the /account route, (src/app/account/page.tsx)
Basically, i am fetching data serever-side, and based on that data we decide what to render to the client. The question here is the way to navigate to this page, is either via <Link> or a router.push using useRouter from (next/navigation), this to keep the SPA-like aspect of the page, and not cause the states to re-render.
But it looks like this page is getting cached whenever we use these methods, and the data-fetching is not occurring on each request. How can i make it occurr on each request?
The function below fetchDataServerSideBeforeRenderingPage() is an axios fetch operation with 'Cache-Control': 'no-store' header.
This is the code of the page that is being rendered on the server:
I'm in the app directory, and i have a page for the /account route, (src/app/account/page.tsx)
Basically, i am fetching data serever-side, and based on that data we decide what to render to the client. The question here is the way to navigate to this page, is either via <Link> or a router.push using useRouter from (next/navigation), this to keep the SPA-like aspect of the page, and not cause the states to re-render.
But it looks like this page is getting cached whenever we use these methods, and the data-fetching is not occurring on each request. How can i make it occurr on each request?
The function below fetchDataServerSideBeforeRenderingPage() is an axios fetch operation with 'Cache-Control': 'no-store' header.
This is the code of the page that is being rendered on the server:
'use server'
import React from 'react';
import fetchDataServerSideBeforeRenderingPage from '@/app/..';
async function AccountPage() {
const data = await fetchDataServerSideBeforeRenderingPage();
if (data){
return (
<>
<h1>Access Granted </h1>
</>
)
} else {
return <h1>Unauthorized Access</h1>;
}
}
export default AccountPage81 Replies
sudoOP
I tried using a router.push() followed by a router.refresh() as a workaround, but i think this could cause race conditions.
Also, i tried using revalidatePath(), beforep pushing to the path, but this will cause two renders (two data-fetches).
Also, i tried using revalidatePath(), beforep pushing to the path, but this will cause two renders (two data-fetches).
via <Link> or a router.push using useRouter from (next/navigation), this to keep the SPA-like aspect of the page, and not cause the states to re-render.You can disable prefetching on your <Link>
But it looks like this page is getting cached whenever we use these methods, and the data-fetching is not occurring on each request. How can i make it occurr on each request?
also remove this
@ᴉuɐpɹɐɐ > via <Link> or a router.push using useRouter from (next/navigation), this to keep the SPA-like aspect of the page, and not cause the states to re-render.
> But it looks like this page is getting cached whenever we use these methods, and the data-fetching is not occurring on each request. How can i make it occurr on each request?
You can disable prefetching on your <Link>
sudoOP
Hey, even if i disable prefetch, the data appearing will still be stale data.
What i am trying to achieve here is exactly what we had in the pages router where we used getServerSideProps.
So basically, the behavior needed is to fetch data on each visit to this /account route. On each request. And for it to be rendered on the server.
Its weird how i can't find anything about this issue online and no one knows anything about it :/
What i am trying to achieve here is exactly what we had in the pages router where we used getServerSideProps.
So basically, the behavior needed is to fetch data on each visit to this /account route. On each request. And for it to be rendered on the server.
Its weird how i can't find anything about this issue online and no one knows anything about it :/
Also, i want to keep the 'use sever' statement, to make sure that the component is explicitly rendered on the server
@sudo Also, i want to keep the 'use sever' statement, to make sure that the component is explicitly rendered on the server
the "use server" is not for ensuring it is rendered on the server.
You are looking for
You are looking for
import "server-only"@ᴉuɐpɹɐɐ the "use server" is not for ensuring it is rendered on the server.
You are looking for `import "server-only"`
sudoOP
yep, i tried using import 'server-only' with a 'use server' for a file that has functions intended to run on the server, for some reason they do not work together :/
@sudo yep, i tried using import 'server-only' with a 'use server' for a file that has functions intended to run on the server, for some reason they do not work together :/
im sorry i dont address the real issue but "use server" is not used to make function run on the server. "use server" will create an endpoint in your next.js server which is why its not advisable to be used as a directive "to make stuff only run in the server"
A Server Action can be defined with the React "use server" directive. You can place the directive at the top of an async function to mark the function as a Server Action, or at the top of a separate file to mark all exports of that file as Server Actions.
yes, its to make "Server Actions" which are different from "function that is only used in the server"
sudoOP
Doesn't a server action create an endpoint, and this endpoint runs a specific logic (Function). So it's actually a server function, no ?
Server Actions are server endpoints that is accessed via the client, if you only used them in server environment then its better to not use Server Action
is what im saying
sudoOP
Okay..
my question here if i want a page to be rendered on the server, it shouldn't have a 'use server' on top of it?
the same way i added in this example
This marks it as a Server Component
something different from server actions
Assume
AccountPage is a server component, then fetchDataServerSideBeforeRenderingPage is just a regular async function. if its not used in the client component (or <form>) then it doesnt have to use "use server" on itsudoOP
aha
if i want a page to be rendered on the server, it shouldn't have a 'use server' on top of it?yes.
page.jsx and layout.jsx are by default server component.
then the determination of server and client is decided from the root file (page.js/layout.js)
then the determination of server and client is decided from the root file (page.js/layout.js)
sudoOP
okay, since page.tsx is by default a server component, adding a 'use server' is the same, no ?
My question here is, how do i get
fetchDataServerSideBeforeRenderingPage() function to run each time this /account page is navigated to. (Using <Link> or a useRouter navigation). That was the problem hereAlso, if page.tsx is by default a server component, then adding 'use server' to it won't change anything
yeah but it is exposing whatever async component as an endpoint.. which is obscured, technically accessible for anyone. could be a security concern.
The client-side caching configuration is still in beta right now (undocumented) but you can try
But it looks like this page is getting cached whenever we use these methods, and the data-fetching is not occurring on each request. How can i make it occurr on each request?This is because the client side navigation keeps a local (per browser) cache that lasts for 30s after your last navigation.
The client-side caching configuration is still in beta right now (undocumented) but you can try
const nextConfig = {
experimental: {
staleTimes: {
dynamic: 0, // set this to 0
static: 180,
},
},
}theres also one other option, which is to use
useEffect to call the data fetching every "component mount" which is automatically handled by React Query. This is however, less than ideal as it could get spammy (though hard to get it right with proper configuration) and even harder to implement with raw useEffect@ᴉuɐpɹɐɐ yeah but it is exposing whatever async component as an endpoint.. which is obscured, technically accessible for anyone. could be a security concern.
> But it looks like this page is getting cached whenever we use these methods, and the data-fetching is not occurring on each request. How can i make it occurr on each request?
This is because the client side navigation keeps a local (per browser) cache that lasts for 30s after your last navigation.
The client-side caching configuration is still in beta right now (undocumented) but you can try
tsx
const nextConfig = {
experimental: {
staleTimes: {
dynamic: 0, // set this to 0
static: 180,
},
},
}
sudoOP
Can you please further explain about this security concern, i didnt get you
"use server" basically makes all the exported async component as a public endpoint. even if its obsecure, someone could theorhetically access whatever exported in that file directly (without accessing the page). This makes it hard to control.
Not to mention that all exported funciton get automatically converted to async function (even if you dont use
Not to mention that all exported funciton get automatically converted to async function (even if you dont use
async) which is why its worse for consistencyits just that you used something thats not what its designed for, i just want to ensure consistencies in your code base
sudoOP
wow
okay
we have an article here
https://joulev.dev/blogs/when-not-to-use-use-client-and-use-server
https://joulev.dev/blogs/when-not-to-use-use-client-and-use-server
sudoOP
i will take a look at that
I agree the names are confusing so im just trying to not blame you for it
sudoOP
now how can i implement this /account page to be a protected route in NextJS then and rendered on the server?
you should prioritise on the
import "server-only" and if you have any import errors it must be that you imported a "server-only" file to a client component (reminder: this doesnt mean component that ONLY uses "use client", other component that suddenly becomes client component can also cause this error)if you have a function that can be used in both server and client, its better to move it away from
import "server-only"move it away: move it into a new file
sudoOP
basically what i am doing is the following:
Frontend: NextJS 14 - React
Backend: ExpressJS - MongoDB
Backend: ExpressJS - MongoDB
Im having hard time with authentication, protected pages ...
basically all requests should be validated on the backend
so what i want to have in /account is a server side rendered component, that will call the backend to make sure this session should access this page or no
and all of this should be done on the server, away from the client
yes
you can make a function that checks your express backend that is marked with "server-only".
this way you 100% ensure this function wont run in the client.
this way you 100% ensure this function wont run in the client.
easy as that
sudoOP
fetchDataServerSideBeforeRenderingPage() i will make this an import server-only functioni wouldnt make my guaranteed server component (like page.js or layout.js) with "server-only" but thats just preference
@ᴉuɐpɹɐɐ i wouldnt make my guaranteed server component (like page.js or layout.js) with "server-only" but thats just preference
sudoOP
if i remove the 'use server' from page.tsx (/account), and run the function that is
import server-only here to make sure that this session has access to this endpoint, this will be good security-wise, right?and since you mentioned page.tsx is by default server-side rendered, everything will happen server-side
it will break your entire app if your "server-only" funciton are accidentally imported to your client-component and you will notice it at development. Since its handled via bundling and not by programmatic logic
sudoOP
tsc won't catch that?
no
idk maybe
but it will break your entire app, which is good enough coz you really cant import it to client-js
sudoOP
lol
@sudo and since you mentioned page.tsx is by default server-side rendered, everything will happen server-side
yes. if you dont believe this, add
console.log() in your component and see where it gets loggedits also in the docs
sudoOP
sometimes i think life was much better in the pages router
i havent used the "server-only" in a while but last time i used it, it prevents me from importing the function to client-js.
It even works if its like not a direct import
if funcA has "server-only"
and funcB imports funcA, separate file, doesnt have "server-only"
and (client) func C imports funcB, it will throw an error
It even works if its like not a direct import
if funcA has "server-only"
and funcB imports funcA, separate file, doesnt have "server-only"
and (client) func C imports funcB, it will throw an error
sudoOP
yes, they get nested somehow 🙂
@sudo sometimes i think life was much better in the pages router
its much easier not because its "pages" router, but because of the paradigm that we are moving away from. The old way feels better because the entire thing is a (server rendered) client component.
Not as good if we want a server-rendered components that is still beautifull even if we disable javascript
Not as good if we want a server-rendered components that is still beautifull even if we disable javascript
the new paradigm that we're moving into is the harmonization of server and client. It can be a more efficient space, at the cost of more complicated paradigm
sudoOP
exactly
we should have a well documentated page about everything that is use server, server actions, server functions etc...
one spot, to read everything
before exploring
not like the next docs, it is somehow overwhelming
i hope they improvve it
or at least add this section
sudoOP
omw
but ultimately this is React's feature not Next.js's
sudoOP
yes
you can find this "paradigm" in more and more framework too,
Redwoodjs
waku,
hono..
and more coming soon
Redwoodjs
waku,
hono..
and more coming soon
maybe this needs to be more clear specifically what part of "next.js" that is defaulted to "Server Component"
sudoOP
yes