My page is working correctly, but When I look in the console I'm getting 5, 404 errors on fetch request. not sure where this is coming from..
I only get these 404s in production. Nothing in development.
Page File Structure
live site: https://real-fake-store.vercel.app/
github repo: https://github.com/Haviles04/real-fake-store
Here is the code I think it's coming from.
export async function getStaticPaths() {
const { products } = await import("../../../data/products/allData.json");
return {
paths: products.map((item) => {
const productId = item.id;
const productName = item.title
.toLowerCase()
.replace(/\s/g, "")
.toString();
const catName = item.category.name.toLowerCase().toString();
return {
params: {
catName,
productId: `${productId}=${productName}`,
},
};
}),
fallback:false,
};
}
export async function getStaticProps({ params }) {
const { categoryItems } = await import(
`../../../data/products/${params.catName}Data.json`
);
const pageProduct = categoryItems.find(
(item) => item.id === parseInt(params.productId)
);
return {
props: {
pageProduct,
},
};
}
and I'm getting this in the console
This was fixed by adding passHref to the LINK component that was directing to the page.
Related
I am using NextJS 13 and performing the following inside the app folder.
I am trying to use generateStaticParams function to achieve static generation pages on build.
This is the route: subpage/[categoryName]/[gifId]
So the route could be like following examples.
/subpage/fashion/1
/subpage/fashion/2
/subpage/fashion/3
/subpage/technology/1
/subpage/technology/2
/subpage/technology/3
/subpage/technology/4
... and so on.
The route subpage/[categoryName] won't have anything there. Might show an error or redirect some place.
The full path subpage/[categoryName]/[gifId] including the [gifId] is a must.
I need to perform REST requests to get the data for the pages.
How could I set this up inside my page.tsx file which will be located at: subpage/[categoryName]/[gifId]/page.tsx ?
If it was a single dynamic path, would be straight forward. See my implementation below for that.
But since is nested with 2 dynamic paths [categoryName] and [gifId] back to back, bit confused how to achieve this. Pls assist.
import MyComponent from "../../../components/MyComponent";
import { PartialGifProps, TagType} from "../../../utils/typings";
import axios from "axios";
import {apiDomain, defaultHeaders} from "../../../utils/constants";
const perPage = 40;
type Props = {
params: {
gifId: string,
},
}
export const generateStaticParams = async () => {
const url = `${apiDomain}/get_gif_count`; // I have access to modify the backend for this if it should contain category.
const fetchGifs = await axios.get(url, { headers: defaultHeaders });
const { total_count: totalCount } : TagType = fetchGifs.data;
const totalPages = Math.ceil(totalCount / perPage);
let paramsList = [];
for (let i = 1; i <= totalPages; i++) {
paramsList.push({ gifId: i.toString() })
}
// this paramsList would look like:
// [
// { gifId: '1', },
// { gifId: '2', },
// { gifId: '3', },
// .......
// ]
return paramsList;
}
const MyPage = async ({params: {gifId}}: Props) => {
const url = `${apiDomain}/get_partial?page=${gifId}&per_page=${perPage}`;
const fetchGifs = await axios.get(url, { headers: defaultHeaders });
const { gifs } : PartialGifProps = fetchGifs.data;
return (
<div className='text-white'>
<MyComponent gifs={gifs}/>
</div>
);
};
export default MyPage;
You can get categoryName in the same way you get gifId, through the params prop
type Props = {
params: {
gifId: string,
categoryName: string,
},
}
const MyPage = async ({params: {gifId, categoryName}}: Props) => {
console.log('categoryName =', categoryName);
const url = `${apiDomain}/get_partial?page=${gifId}&per_page=${perPage}`;
const fetchGifs = await axios.get(url, { headers: defaultHeaders });
const { gifs } : PartialGifProps = fetchGifs.data;
return (
<div className='text-white'>
<MyComponent gifs={gifs}/>
</div>
);
};
I have the following content structure:
src
|-- content
|-- terms-conditions.html
|-- makeup.html
The terms and conditions page has the following data which I can call
{ seo:
url: "makeup/terms-conditions"
}
I'd like the content of terms-conditions.html to actually show under the url makeup/terms-conditions. I've tried a few times using getStaticPaths to no avail so any guidance would be appreciated. Essentially all of my files will sit one level down from content but I'd like to actually serve them under the URL defined under seo/url
My getStaticPaths code in my [params].tsx file is as follows.
export async function getStaticPaths() {
const posts = getAllPosts(["slug"]);
return {
paths: posts.map((post) => {
return {
params: {
slug: post.slug,
},
};
}),
fallback: false,
};
}
getStaticProps:
export async function getStaticProps({ params }) {
const post = getPostBySlug(params.slug, ["slug", "data"]);
const content = (await post.content) || "";
return {
props: {
post: {
...post,
content,
},
},
};
}
And in my api file I have:
import fs from "fs";
import { join } from "path";
import matter from "gray-matter";
const postsDirectory = join(process.cwd(), "src/content");
export function getPostSlugs() {
return fs.readdirSync(postsDirectory);
}
export function getPostBySlug(slug: string, fields = []) {
const realSlug = slug.replace(/\.html$/, "");
const fullPath = join(postsDirectory, `${realSlug}.html`);
const fileContents = fs.readFileSync(fullPath, "utf8");
const { data, content } = matter(fileContents);
const items = { };
fields.forEach((field) => {
if (field === 'slug') {
items[field] = realSlug
}
if (field === 'data') {
items[field] = data
}
})
return items;
}
export function getAllPosts(fields = []) {
const slugs = getPostSlugs()
const posts = slugs
.map((slug) => getPostBySlug(slug, fields))
return posts
}
I'm fetching page data from a cms, so far I have only one page in pages/posts.
pages/posts/[slug].js
import { getAllPostsWithSlug, getPostAndMorePosts } from '../../lib/api';
export default function Post({ post }) {
const router = useRouter();
const { slug } = router.query;
return (
<div>
<p>
title: {typeof post == 'undefined' ? 'no post' : post.title}
</p>
</div>
);
}
export async function getStaticProps({ params, preview = null }) {
const data = await getPostAndMorePosts(params.slug, preview);
const content = await markdownToHtml(data?.posts[0]?.content || '');
return {
props: {
preview,
post: {
...data?.posts[0],
content,
},
morePosts: data?.morePosts,
},
};
}
export async function getStaticPaths() {
const allPosts = await getAllPostsWithSlug();
return {
paths: allPosts?.map((post) => `/posts/${post.slug}`) || [],
fallback: true,
};
}
That will correctly display post.title, but if I access the property directly with
<p>title: {post.title}</p>
I get the build error:
post undefined
Is next trying to build a page out of the template with no data? When the build succeeds I only have one route in /posts.
I'm new to Vue.JS and JavaScript, so I have awful times debugging these applications, specially with promises and asynchronous tools. I'm trying to build my first Vue component that fetches data from somewhere. I'm using the Google Sheets API and returning some cells of a sample sheet. My component looks like this:
<template>
<ul>
<li v-for="athlete in athletes" :key="athlete">
{{ athlete }}
</li>
</ul>
</template>
<script>
import readCopaPinheiros from '#/sheets/fetchData.js';
export default {
name: 'AthletesTable',
data () {
return {
loading: false,
athletes: null
}
},
created () {
this.fetchData()
},
methods: {
fetchData() {
this.loading = true;
readCopaPinheiros('inscritos').then(values => {
this.loading = false;
console.log(values)
this.athletes = values
});
},
}
}
</script>
<style>
</style>
EDIT 1
The fetchData script:
const fs = require('fs');
const { google } = require('googleapis');
const TOKEN_PATH = '';
const CREDENTIALS_PATH = ''
const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf-8'));
const {
client_secret: clientSecret,
client_id: clientId,
redirect_uris: redirectUris,
} = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
clientId, clientSecret, redirectUris[0],
);
const token = fs.readFileSync(TOKEN_PATH, 'utf-8');
oAuth2Client.setCredentials(JSON.parse(token));
async function readSheet(spreadsheetId, range) {
const sheets = google.sheets({ version: 'v4', auth: oAuth2Client });
return sheets.spreadsheets.values.get({
spreadsheetId,
range,
})
.then(res => res.data.values)
.catch(err => console.log('Opa! Erro:', err));
}
function readSheetJsnofied(spreadsheetId, range) {
return readSheet(spreadsheetId, range)
.then(values => jsonifySheet(values));
}
function jsonifySheet(sheetValues) {
const header = sheetValues[0];
const values = sheetValues.slice(1);
return values.map((row) => {
const rowObj = ({});
for (let i=0; i < row.length; i++) rowObj[header[i]] = row[i];
return rowObj;
});
}
const readCopaPinheiros = d => readSheetJsnofied('sheetId', d);
export default readCopaPinheiros
For some reason the component doesn't render. I don't know what to do even to debug, all my console log tries never prints something to the console. Could someone help me understand what is going wrong?
EDIT 2
This error just shows up when trying to fetch data:
When I try to use a placeholder list with fake values directly in the data function it works. I don't believe that is a problem with the rendering itself, but how it interacts with the created and fetchData functions.
v-for="athlete in athletes"
This code only works when the athletes is an array. Initially, you set it as null so until the data from api is arrived, it will be null.
But the component still tries to render the component with your null athletes and will make the error.
You can try with this solution:
data () {
return {
loading: false,
athletes: []
}
},
I have a problem with the getStaticPaths function.
When I try to get a dynamic display with a parameter it shows me as error: A required parameter (id) was not provided as a string in getStaticPaths for / movies / [id] but if I use the other way above it works. Above all I am the documentation.
import fetch from 'node-fetch';
function MovieSelect({movie}){
return(
<div>
<h1>Test: {movie.name}</h1>
<p>{movie.summary.replace(/<[/]?[pb]>/g, '')}</p>
{movie.image ? <img src={movie.image.medium} /> : null}
</div>
)
}
export async function getStaticPaths(){
const request = await fetch('https://api.tvmaze.com/search/shows?q=batman')
const movies = await request.json()
//const paths = movies.map(movie =>`/movies/${movie.show.id}`)
const paths = movies.map(movie =>({
params: {id: movie.show.id},
}))
return {
paths,
fallback: false
}
}
export async function getStaticProps({params}){
const request = await fetch(`https://api.tvmaze.com/shows/${params.id}`)
const movie = await request.json()
return{
props:{
movie
}
}
}
export default MovieSelect
A required parameter (id) was not provided as a string in getStaticPaths for / movies / [id]
id should be a string as suggested by the error. Upon hitting the api from your browser, you can see that the id is not a string but a number. You need to convert it to string.
params: {id: movie.show.id.toString()},
My problem generated the same error, but I had a different bug.
TL;DR: The name of my file needed to match the key of the slug used in the params object.
In my case, my file name was [postSlug].js. Therefore, the key should have been postSlug inside of getStaticPaths().
// In [postSlug].js
const pathsWithParams = slugs.map((slugs) => ({ params: { postSlug: slug } })); // <-- postSlug is right
const pathsWithParams = slugs.map((slugs) => ({ params: { id: slug } })); // <--- id is wrong
My entire function then looked like this
export async function getStaticPaths() {
const slugs = await getAllBlogSlugs();
const pathsWithParams = slugs.map((slug) => ({ params: { postSlug: slug } }));
return {
paths: pathsWithParams,
fallback: "blocking",
};
}
References:
NextJS.org Get Static Paths
export async function getServerSideProps({ query, locale }) {
const { id } = query;
if (!id) return { notFound: true };
return {
props: {
fallback: true,
query,
locale,
...(await serverSideTranslations(
locale,
["common", "header", "footer"],
nextI18nextConfig
)),
},
};
}