So, I am working on a simple Next.Js app. It's just a Lyrical Page but I'm running onto some issues. I'm new to Next.Js and this is my first project. So, here is my problem:
I have a dynamic set of pages/folders which looks like this:
songs (empty folder)
[singer] (child of songs containng just an index.jsx)
[title] (child of [singer] containing another index.jsx)
Now in my [title] - index.jsx im rendering a simple page providing lyrics for a specific song.
My problem is that I want to count views (every time someone opens this page) for each song sepertly. I have the following code:
export const getStaticProps = async (context) => {
const res = await fetch(`${process.env.PROXY}api/lyrics/post/${encodeURIComponent(context.params.title)}`);
const data = await res.json();
const send = await fetch(`${process.env.PROXY}api/lyrics/views/${data.post._id}`);
return {
props: {
data: data.post,
message: 200
}
}
}
export const getStaticPaths = async () => {
const res = await fetch(`${process.env.PROXY}api/lyrics/getAll`);
const data = await res.json();
const paths = data.all.map((name) => ({ params: { singer: name.singer.toString(), title: name.title.toString() } }));
return {
paths: paths,
fallback: 'blocking'
}
}
The problem is I know getStaticProps renders only on build time, however, I want to render every time so that I can count the views with my send variable.
Can someone please help me figure this out? Any help will be appreciated!
Related
Been experimenting with some server components and nextjs (v13) and have encountered something that i'm not sure how to do. Let's say in theory I wanted to have something with this functionality (with the requests running serverside)
const ClientComponent = () => {
// this value cannot be hardcoded into the server component, nor can we change
// this value at any point in time during the clients components life (just using const for example)
// in reality it'd be set in some other format
const id = "some-id"
return <ServerComponent id={somethingId} />
}
const fetchData = async (id: string) => {
// writing this off top of head, not sure if correct syntax but some basic post request using the id
const res = await fetch("someUrl", { data: id });
const data = await res.json();
return { data };
}
const ServerComponent = async ({ id }: { id: string }) => {
if (!id) return null;
const { data } = await fetchData(id);
return (
<div>
{data.someValue}
</div>
);
}
How would I go about doing something of this nature? is this considered "improper" / "bad practice"? if so what would be a better way to go about doing this? Keep in mind that ClientComponent could be three-four nodes down (with each node being a client component)
Thanks :)
I have a problem figuring out how to localize my content with Next.js and Strapi.
my current file structure looks like so:
pages/
blog/
- [post].tsx
- portfolio.tsx
Inside portfolio I am fetching data for all posts created on Strapi, depending on which locale is currently set, like so:
export const getStaticProps = async ({ locale }) => {
const res = await fetch(
`https://strapi.com/api/articles?locale=${locale}&populate=*`
);
const data = await res.json();
return {
props: {
articles: data.data,
},
};
};
In [post] it looks like that:
export const getStaticPaths = async ({ locales, locale }) => {
const res = await fetch(
`https://strapi.com/api/articles?locale=${locale}&populate=*`
);
const data = await res.json()
const ids = data.data.map((post: any) => post.id);
const paths = ids
.map((id: any) =>
locales.map((locale: any) => ({
params: { post: id.toString() },
locale,
}))
)
.flat();
return {
paths,
fallback: false,
};
};
export const getStaticProps = async (context: any) => {
const id = context.params.post;
const res = await fetch(
`https://strapi.tincors.com/api/articles/${id}?populate=*`
);
const res_blocks = await fetch(
`https://strapi.tincors.com/api/articles/${id}?populate[blocks][populate]=*`
);
const data = await res.json();
const blocks_data = await res_blocks.json();
const block_data_slider = blocks_data.data.attributes.blocks[0].files.data;
return {
props: { article: data.data, slider: block_data_slider },
};
};
Note that each article on Strapi has different id for it's localized version - so as an example:
article_1_pl has id of 10
but it's english variant has id of 12.
fun fact - on portfolio.tsx data is fetched successfully, and post miniature cards are properly displaying based on the current domain (on dev I am using two different hosts for i18n - localhost:3000 for PL & example.com:3000 for EN).
However once I try to redirect myself to the full article, by clicking on the post miniature card, I get the 404 page not found error in the browser, with these errors poping each second in the console logs Error.
It doesn't matter which local host I am currently at.
cards are wrapped in a <Link href="/blog/${id}" locale={route.locale} />
However the moment I change my code in [post].tsx by removing the "locale" from the endpoint:
const res = await fetch(
`https://strapi.com/api/articles?locale=${locale}&populate=*`
);
to:
const res = await fetch(
`https://strapi.com/api/articles?populate=*`
);
suddenly each of my PL articles on localhost:3000 are displayed properly, and only the english variants aren't working (404 page not found).
I assume that it is due to the fact, that by removing the "locale" from the API endpoint it only fetches the polish articles, not the english ones, but it baffles me why it's not working at all, when I use the localized endpoint source.
How I want my app to work:
I want article description (which is generated as a dynamic route) to display in active language, by fetching localized data by the post id.
My question is:
How do I fetch the proper localized data from Strapi in [post].tsx
when using getStaticPaths & getStaticProps. What is wrong with above code and how do I fix it?
I apologize if the above description is too chaotic to understand - feel free to ask for more explanations if necessary :)
I am using Next.js's Static HTML Export for my site which have 10 million static pages but I am running into ram issues when building the app.
Is it even possible to export it in parts like 100k pages on first build then 100k on second build and so on?
I do not want to use Incremental Static Regeneration or getServerSideProps to cut costs.
This site is using MongoDB only have two pages home page and posts page:
index.js
[postPage].js
In home page I used this code:
export async function getStaticProps() {
const { db } = await connectToDatabase();
const postsFeed = await db
.collection("myCollection")
.aggregate([{ $sample: { size: 100 } }])
.toArray();
return {
props: {
postsFeed: JSON.parse(JSON.stringify(postsFeed)),
},
};
}
In posts page I used this code:
export async function getStaticPaths() {
const { db } = await connectToDatabase();
const posts = await db
.collection("myCollection")
.find({})
.toArray();
const paths = posts.map((data) => {
return {
params: {
postPage: data.slug.toString(),
}
}
})
return {
paths,
fallback: 'blocking'
}
}
export async function getStaticProps(context) {
const postSlug = context.params.postPage;
const { db } = await connectToDatabase();
const posts = await db
.collection("myCollection")
.find({ slug: { $eq: postsSlug } })
.toArray();
const postsFeed = await db
.collection("myCollection")
.aggregate([{ $sample: { size: 100 } }])
.toArray();
return {
props: {
posts: JSON.parse(JSON.stringify(posts)),
postsFeed: JSON.parse(JSON.stringify(postsFeed)),
},
};
}
Doesn't seem to be a built-in option to process batches of static pages https://github.com/vercel/next.js/discussions/14929
I can only think of dividing the work using a bash script where you set an env variable and use it in the code where you're fetching the data to generate the paths, then run the build command as many times as parts you need to split the data, in each iteration move the generated files to another directory that will be your consolidated output.
COUNTER=1
PARTS=100 # change it to control number of parts
while [ $COUNTER -lt $PARTS ]; do
let COUNTER=COUNTER+1
CURRENT=$COUNTER PARTS=$PARTS next build
# move generated files to another directory
done
in your get getStaticPaths
export async function getStaticPaths() {
const currentPercentage = process.env.CURRENT/process.env.PARTS
// logic to fetch the corresponding current percentage of the data
// 1% when there are 100 parts, 0.5% when 200 parts, etc.
}
Be aware that if the data changes very often, you'll see incorrect results, like repeated pages or skipped ones, since each pagination will occur at different moments when running the script. I believe you could create an auxiliary node (or another language) script to better handle that quantity of records, maybe in a streamlined way, and generate JSON files for each chunk of data to use them in getStaticPaths instead of fetching them directly from the DB.
I have a problem that my website being deployed to Vercel with NextJS ISR. When I use getStaticPaths. The website will reload unlimited when I click to getStaticPaths that page.
Only getStaticProps will not make this error. I am using the hobby plan. Everything works when I am on localhost and use "npm run build and start".
here is my code:
export async function getStaticProps({ params: { slug } }) {
const res = await fetch(`${API_URL}/post/${slug}`);
const posts = await res.json();
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
revalidate: 10, // In seconds
};
}
export async function getStaticPaths() {
// this URL is created specifically for fetch first time of ISR
// My first post will unlimited reload**********
const res = await fetch(`${API_URL}/postforisrfirst`);
// const res = await fetch(`${API_URL}/post`);
const posts = await res.json();
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { slug: post.slug },
}));
return { paths, fallback: "blocking" };
}
I need to visit the pagination URLs to get every list item in them and get all lists in one big array of objects to be able to pre-render in getStaticPaths(). After little thinking I decided to use for(..) loop in. Since the query string just asks for page number in the URL, I decided it would be the right solution.
My code:
export async function getStaticPaths() {
let bookPages = [];
for (let pageNo = 1; pageNo=5; pageNo ++) {
const res = await axiosInstance.get(`/?page=${pageNo}`);
const resultsList = await res.data.results;
bookPages.push(resultsList);
}
const paths = bookPages.map((book) => ({
params: { id: book.id.toString() },
}))
return { paths, fallback: false }
}
There are just 5 pages and no new data will be added. Every result response contains an array that has a list of 30 objects. I want to put all the 30 objects per URL to the one big array called bookPages, totaling 150 objects in the array, and use it to return paths.
When building, Nextjs collects page data for 60 seconds, then runs again as it fails to collect the data and throws this error:
> Build error occurred
Error: Collecting page data for /book/[id] is still timing out after 2 attempts. See more info here https://nextjs.org/docs/messages/page-data-collection-timeout
But if I request for only the first page, it builds properly.
export async function getStaticPaths() {
const res = await axiosInstance.get('/?page=1');
const resultsList = await res.data.results;
const paths = resultsList.map((book) => ({
params: { id: book.id.toString() },
}))
return { paths, fallback: false }
}
Update:
I tried with Promise.all() in this manner but it failed the build:
let bookPages = [];
let links = [];
for (let paginate=1; paginate=5; paginate++) {
links.push(`${process.env.NEXT_PUBLIC_URL}?page=${paginate}/`);
}
let requests = links.map(url => axios.get(url));
Promise.all(requests)
.then(responses => responses.forEach(
response => bookPages.push(response.data.results)
));
const paths = bookPages.map((book) => ({
params: { id: book.id.toString() },
}))