undefined getStaticProps causes build failure on page that doesn't exist - javascript

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.

Related

next.js getStaticProps Serialize issue

I'm using next.js for a project where axios fetch in getStaticProps doesnot seem to work even though the URL is serialised in configuration.I tried serializing again by passing the response to JSON.parse but still cant find a solution.
import axios from "axios";
import Qs from "qs";
My axios config code below:
const axiosTmdbApi = axios.create({
baseURL: "https://api.themoviedb.org/3",
headers: { "content-Type": "application/json/" },
paramsSerializer: {
serialize: (params) =>
Qs.stringify({ ...params, api_key: apiKey }, { arrayFormat: "brackets" }),
},
});```
**My category which is passed as a parameter to invoke getTvList or getMovieList data below:**
import axiosTmdbApi from "./axiosTmdbApi";
export const category = {
movie: "movie",
tv: "tv",
};
export const type = {
top_rated: "top_rated",
popular: "popular",
};
const tmdbApi = {
getTvList: (tvType, params) => {
const url = "tv/" + type[tvType];
return axiosTmdbApi.get(url, params);
},
getMovielist: (movieType, params) => {
const url = "movie/" + type[movieType];
return axiosTmdbApi.get(url, params);
},
};
export default tmdbApi;```
Using getStaticProps to fetch my API
import tmdbApi from "../../api/tmdbApi";
import { type, category } from "../../api/tmdbApi";
const Movies = ({ data }) => {
console.log(data);
return (
<>
<h1 className="bg-success">Movies</h1>
</>
);
};
export default Movies;
export async function getStaticProps() {
let params = {};
let response;
response = await tmdbApi.getMovielist(type.popular, {
params,
});
const data = JSON.parse(JSON.stringify(response));
return {
props: { data },
};
}```
**Error :index.js?46cb:602 Uncaught TypeError: Converting circular structure to JSON
--> starting at object with constructor 'ClientRequest'
| property 'socket' -> object with constructor 'TLSSocket'
--- property '_httpMessage' closes the circle **
Try adding console.log and see what values are being handled at each stage. Instead of const data = JSON.parse(JSON.stringify(response)), you should be doing const data = response.data.
and change return statement to
return {
props: { data: data || [] },
};

Getting stray fetch request to JSON files while running getStaticProps/ getStaticPaths

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.

How to show content under a different URL to the file name using next.js

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
}

json fetch in vue not working as it should

I'm building a simple test for a project of mine after watching a bunch of vue.js lessons.
My test is simple, fetch some json from an api i created with express.js and output it on the screen, I wanted do it the proper way so i build all the little components that make up my test homepage and commited the fetch through dispatching an action, this is the structure:
my submissions/action.js
export default {
async loadSubs(context) {
const res = await fetch(`http://localhost:3001/api`, {
mode: "no-cors",
});
console.log(res);
const resData = await res.json();
console.log(resData.name);
if (!res.ok) {
console.log(resData);
const error = new Error(resData.message || "failed to fetch");
throw error;
}
const subsList = [];
for (const key in resData) {
const sub = {
name: resData[key].name,
};
subsList.push(sub);
}
context.commit("setSubs", subsList);
},
};
my submission/mutation.js:
export default {
setSubs(state, payload) {
state.submissions = payload;
},
};
they get imported in submissions/index.js :
import mutations from "./mutations.js";
import actions from "./actions.js";
import getters from "./getters.js";
export default {
namespaced: true,
state() {
return {
submissions: [
{
name: "",
},
],
};
},
mutations,
actions,
getters,
};
and submission/index.js gets imported in store/index.js
import { createStore } from "vuex";
import SubmissionsModule from "./modules/Submissions/index.js";
const store = createStore({
modules: {
submissions: SubmissionsModule,
},
});
export default store;
My vue components are the following(i'm leaving out the css)
BaseCard.vue
<template>
<div class="card">
<slot></slot>
</div>
</template>
my Submission/SingleSubmission.vue
<template>
<div class="subList">
<base-card>
<h2>{{ name }}</h2>
</base-card>
</div>
</template>
<script>
export default {
props: ["name"],
};
</script>
and this is my views/Home.vue:
<template>
<div>
<single-submission></single-submission>
</div>
</template>
<script>
import SingleSubmission from "../components/SingleSubmission.vue";
export default {
components: {
SingleSubmission,
},
computed: {},
created() {
this.loadSubmission();
},
methods: {
async loadSubmission() {
//this.isLoading = true;
try {
await this.$store.dispatch("submissions/loadSubs");
} catch (error) {
this.error = error.message || "something went wrong";
}
// this.isLoading = false;
},
},
};
</script>
The api is just sending back a line of json, just to test if i can render something.
const express = require("express");
const app = express();
const apiPrefix = "/api";
//Define the root endpoint
app.get(apiPrefix, async (req, res) => {
console.log(req.headers);
res.json({
name: "test",
});
});
app.listen(3001, () => {
console.log("listening on port 3001");
});
RESULTS OF WHAT I'VE DONE:
this is the result of res object when i console log it:
and this is the network tab on the browser i use:(fun fact:on Brave and Chrome the response tab is empty, while on firefox, i can see the json intended to see, but only in the developer tools reponse tab )
In the end the response status is 200 but i get nothing from the fetch expept res but console.log(resData.name) don't even gets executed and nothing is printed on the screen.
I really don't know what to do because it's seems such a stupid thing and I can't get around it.

Getting "A required parameter (id) was not provided as a string in getStaticPaths" error in Next.js

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
)),
},
};
}

Categories

Resources