I am using the Wordpress REST API to generate a static page with Next JS, bue when I update the content on the admin, Next JS keeps fetching a cached version of the JSON.
What I did was to add a &t=[timestamp] to the url on getStaticProps fetch:
export async function getStaticProps(context) {
const res = await fetch(process.env.NEXT_PUBLIC_WP_URL + '/posts?_embed&per_page=91&order=asc&t=' + Math.round(new Date().getTime()/1000))
const noticias = await res.json()
return {
props: {
noticias,
},
}
}
I guess it shoud be a better solution for this.
Related
I'm trying to understand when exactly does getStaticProps fetch the data.
is it on the build process or every time the website is been render to someone (every time someone visits my app)
if it's the first one, doesn't it make my app "static" meaning it won't update the data when the data on my DB is changed?
Thanks!!
I am going to sleep So I just copied below answer from NextJS Docs
Next.js allows you to create or update static pages after you’ve built your site. Incremental Static Regeneration (ISR) enables you to use static-generation on a per-page basis, without needing to rebuild the entire site. With ISR, you can retain the benefits of static while scaling to millions of pages.
To use ISR add the revalidate prop to getStaticProps:
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: blocking } will server-render pages
// on-demand if the path doesn't exist.
return { paths, fallback: 'blocking' }
}
Do I need to use getStaticPaths if I'm not going to run the "next export" command?
Can I create a cache structure on the server side using getStaticProps and revalidate for the detail page (/user/1)? Or is there no alternative other than using SWR or getServerSideRender?
Isn't it redundant to get all the data again in the detail(/user/1) page?
Note: User detail page can be refreshed every 60 seconds. Instant update is not important.
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
const paths = posts.map((post) => ({
params: { id: post.id },
}))
return { paths }
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
return { props: { post } }
}
No . getStaticPaths is useful when it comes to statically generating lot's of pages incrementally .
Yes in next js you can use revalidate cache strategy only when you are generating static pages with getStaticProps like this :
export async function getStaticProps({ params }) {
const res = await fetch(`https://.../posts/${params.id}`);
const post = await res.json();
// revalidate value is basically in seconds so here it will revalidate the
// data every 1 second only If there is an update to it .
return { props: { post } , revalidate : 1 }
}
You can use getStaticProps method with getStaticPaths to incrementally generate all pages you need and use pagination .
I have a page where I need to fetch data with axios which contains credentials and depending on the parameter in the url make another request using the data from the previous request.
I don't know how I can approach this since I can use both getInitialProps and getStaticProps in pages, and in components I can't use them at all.
The code below works, but I don't know how to break it down so that the login is done on the servar side so that I can still get parameters from the URL.
function Result({surveyId, responseId, sessionKey}) {
return (
<>
<div>surveyId: {surveyId}</div>
<div>responseId: {responseId}</div>
<div>sessionKey: {sessionKey}</div>
</>
)
}
Result.getInitialProps = async ({ query }) => {
// Example of URL
// http://localhost:3000/result?surveyId=12345&responseId=6
const surveyId = query.surveyId
const responseId = query.responseId
const data = { method: 'get_session_key', params: ['admin', 'password'], id: 1 }
const options = {
headers: {
'connection': 'keep-alive',
'content-type': 'application/json'
}
}
const sessionKey = await axios.post(url, data, options).then(
(response) => {
return response.data.result
},
(error) => {
console.log(error)
}
)
return {
surveyId: surveyId,
responseId: responseId,
sessionKey: sessionKey,
}
}
getStaticProps, getInitialProps or getServerSideProps, they all executed only in the pages. Because client makes the request to the pages, so behind the scene, the way how next.js sets up the routing, whenever a request hits a route, next.js first checks if this page has any of those function, and if it has it runs those functions first, gets the results as props and then runs the component functions and passes the props to the component.
getsStaticProps is used for static file generation. A good example is generating of blogs. When you run npm run build,next js will run all api calls and populate the page with blogs. So actually, if you check the build folder, inside html files of pages that getStaticPath is executed, all the data will be already inside that html file. Data will be cached by the server so when user makes a request to those statically generated files, data will be served right away. So you should not run the login process in the getStaticProps since login is a dynamic process.
All those functions are used for prerendering for better SEO optimization. But if you want to load user-specific data you dont need to prerender user-specific data for seo purpose. You could just do client-side as well.
Or you could use next.js api functions, you would be writing the function inside api directory, and from getServerSideProps you would send a request to the api route. That way, if you need to run same code in a different page, instead of writing the same code for authentication, you would be making request the api function and it would handle for you.
I found another solution which uses getStaticPaths and dynamic routes. In my case /pages/survey/[...params].js.
export default function Result({ surveyId, responseId, sessionKey }) {
return (
<>
<div>surveyId: {surveyId}</div>
<div>responseId: {responseId}</div>
<div>sessionKey: {sessionKey}</div>
</>
)
}
export function getStaticPaths() {
return { paths: [], fallback: true }
}
export async function getStaticProps({ params }) {
const surveyId = params.survey[0]
const responseId = params.survey[1]
const data = { method: 'get_session_key', params: ['admin', 'password'], id: 1 }
const options = {
headers: {
'connection': 'keep-alive',
'content-type': 'application/json'
}
}
const url = 'https://example.com'
const sessionKey = await axios.post(url, data, options).then(
(response) => {
return response.data.result
},
(error) => {
console.log(error)
}
)
return {
props: { surveyId, responseId, sessionKey },
revalidate: false,
}
}
I have a page which requires making an HTTP request to an API which might take more than 10 seconds to respond, and my host limits me to 10 second executions.
Is there a way that I can load a temporary page or something and then asynchronously load the rest of the data? I'm currently doing this:
export async function getServerSideProps({ params }) {
const res = await fetch(`${process.env.API_USER}name=${params['name']}`)
const videos = await res.json()
const tag_res = await fetch(`${process.env.API_TAG}author=${params['name']}`)
const tags = await tag_res.json()
const name = params['name']
return {
props: { videos, tags, name }, // will be passed to the page component as props
}
}
Lets's move your HTTP request from getServerSideProps to client side (your components)
// Functional component
useEffect(() => {
fetch(...)
}, [])
// Class-based component
componentDidMount() {
fetch(...)
}
If you still want to stick with getServerSideProps, maybe you have to upgrade/switch your host, or implement a proxy/wrapper server for handling your HTTP request and return response as fast as it can
I am developing an e-commerce like website using NextJS.
I will fetch & display list of products in /products page. On clicking any product, I'll navigate to /details/[productId], and I'll fetch those product details as follows.
// In /details/[productId].js file
export async function getServerSideProps({params}) {
const res = await fetch(`https:my-api-url/api/products/${params.productId}`)
const product = await res.json()
return {
props: {
product
}
}
}
Problem
Everything looks good till this step. But I thought to reduce number of database read count, hence instead of fetching product detail again in detail page, I planned to use the data fetched in the previous page (/products) which will have the information about the product. Hence I need a way to pass those product object into next screen /details/[productId]'s getServerSideProps (to achieve SSR for SEO purposes).
Workaround
One solution I currently have is to stringify the product json and pass it via query parameter and get it back in getServerSideProps({params, query}). But it just spams my url in the browser which isn't look good at all.
Expectation
Is there any other way to pass the data into getServerSideProps function so that it will utilize the data to generate the whole page in server itself. Please guide me to overcome this issue. Any help would be appreciated.
Thanks in advance.. (:
You can bring in a custom server as express that provides locals property available through the lifetime of your application or request.
const next = require('next');
const express = require('express');
const app = next({ dev: process.env.NODE_ENV !== 'production' });
const handle = routes.getRequestHandler(app);
const env = process.env.NODE_ENV || 'dev';
app.prepare().then(() => {
const server = express();
server.get('/products', async (req, reply) => {
const products = await //... fetch product with details
req.app.locals.products = products;
return app.render(req, reply, '/path/to/products/page', req.query);
});
server.get('/details/:productId', async (req, reply) => {
const {productId} = req.params;
const {products} = req.app.locals;
// find product using productId and make available in req.locals
req.locals.product = // product;
return app.render(req, reply, '/path/to/product/detail/page', req.query)
});
server.get('*', (req, reply) => {
return handle(req, reply)
});
server.listen(3000);
});
Pay caution to how large your product list grow to avoid running your application out of memory.
You could also return a cookie containing the list of products on the request for products (See limits for HTTP cookies). Then read that on the product detail page.
When I enter URL http://localhost:3000/blog/wfe436
//getting the meta tags dynamically
export const getServerSideProps = async ({ params }) => {
// Get external data from the file system, API, DB, etc.
console.log(params) // here is the data of the url { blogname: 'wfe436' }
const posts = Data
// The value of the `props` key will be
// passed to the `Home` component
return {
props: { posts }
}
}