I am trying to set up a dynamic page in Next.js with getStaticPaths(). I have it 90% of the way there, but I can't get the getStaticProps() function to reference the URL query so that it loads the proper info.
My code is:
//Get Static Paths
export async function getStaticPaths() {
const paths = await (
await youtube.get("search?")
).data.items.map((video: any) => {
const id = video.id.videoId;
return { params: { id } };
});
return {
paths: paths.map((path: any) => path),
fallback: false,
};
}
//Get Static Props
export async function getStaticProps(context: any) {
const { query = "" } = context.params.query;
const videos = await (await youtube.get("search?")).data.items;
const video = videos.find((vid: any) => {
return vid.id.videoId === query;
});
return {
props: {
video,
},
revalidate: 10,
};
}
I am receiving the error:
Error: Error serializing `.video` returned from `getStaticProps` in "/media/videos/[id]".
Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
If I replace the 'query' variable in the getStaticProps() function with the actual ID of the video I am trying to load, everything goes through fine and the page loads. But then this is not dynamic, and is not using the url query to match with the proper video like I want.
Here is the working, non-dynamic code with a manually typed in ID to force a match.
//Get Static Props
export async function getStaticProps(context: any) {
// It's important to default the slug so that it doesn't return "undefined"
const { query = "" } = context.params;
const videos = await (await youtube.get("search?")).data.items;
const video = videos.find((vid: any) => {
return vid.id.videoId === "_TACZtT1irI";
});
return {
props: {
video,
},
revalidate: 10,
};
}
How can I make sure that the 'video' variable is accessing the url query? It should be grabbing it with context.params, but that is not working, what am I doing wrong?
I figured it out. This is working.
//Get Static Props
export async function getStaticProps(context: any) {
// It's important to default the slug so that it doesn't return "undefined"
const { query = "" } = context.params;
const videos = await (await youtube.get("search?")).data.items;
const video = videos.find((vid: any) => {
return vid.id.videoId === context.params.id;
});
return {
props: {
video,
},
revalidate: 10,
};
}
Related
I am trying to create a dynamic page in Nextjs app, but continue to get an error:
./somepage/[id].js (20:25) # map
Unhandled Runtime Error
TypeError: Cannot read properties of undefined (reading 'map')
So basically what I did to recreate the issue was
I set up a dynamic page [id].js and tried fetching data from a helper file using getStaticProps and getStaticPaths as shown below:
// "./somepage/[id].js"
import {postData} from "./lib/helper"
function classNames(...classes) {
return classes.filter(Boolean).join(" ");
}
export default function Post({ posts }) {
console.log(posts); //is "undefined".
console.log(postData) // logs out the data successfully
return (
<>
{posts.map((post) =>{
<div key = {post.id}>
Hello {post.author}, This is your title: {post.title}
</div>
})}
</>
);
}
export async function getStaticPaths({ params }) {
const { id } = params;
const posts = postData(id);
const paths = posts.map((item) => {
params: {
id: item.id.toString();
}
return {
paths,
fallback: false,
};
});
}
export async function getStaticProps() {
const posts = postData();
console.log(posts); //Logs nothing to the console
return {
props: { posts }, //This prop `posts` renders as undefined on the page.
};
}
The helper file is a function that returns an array of objects, and filters through the array if the array id matches the id from the params as shown below:
// "./lib/helper.js"
export function postData(id) {
const data = [{
...
...
}]
if(id){
return data.filter((item) => {
return item.id === id;
});
}
return data;
}
The issue I am having is that if I console log data from the helper file from within getStaticProps as shown above, I don't get anything back in the console... nothing, but get the above error in the browser. However, logging postData (from the helper.js file) inside of the dynamic page itself returns data as expected, but logging the prop posts that I passed in the getStaticProps comes out as undefined . My version of NextJs is v.13.. could the version be the reason why i couldn't pass data as props onto the page?
For me it is supposed to be something like that :
export async function getStaticPaths() {
const posts = postData();
const paths = posts.map((item) => {
params: {
id: item.id.toString();
}
});
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
const { id } = params;
const posts = postData(id);
return {
props: { posts },
};
}
I am working on Rectjs,I am using nextjs, I created "[slug.js]" for dynamic routes,But its showing previous data instead of current data(fetching from database via axios),I checked in console.log,and in console showing old data not current,where i am wrong ?Here is my current code
const Post = function ({post}) {
useEffect(() => {
// showing old record/title instead of current
console.log(`Found Post title : ${post.title}`);
}, []);
};
export const getStaticProps = async ({ params }) => {
const { data } = await axios.get(
`https://diggdevelopment.com/blackstallion_new/api/getblogbyuserid/${params.slug}`
);
const post = data;
console.log('dat is' + post);
return {
props: {
post,
},
};
};
export const getStaticPaths = async () => {
const { data } = await axios.get(
'http://diggdevelopment.com/blackstallion_new/api/blogscopy'
);
const posts = data.slice(0, 10);
const paths = posts.map((post) => ({ params: { slug: post.id.toString() } }));
return {
paths,
fallback: true,
};
};
I am not getting any error when browsing page or url but when trying to npm run build getting this error Generating static pages (0/8)TypeError: Cannot read properties of undefined (reading 'blog_slug')
here is my full code:
I aslo want to show 404 page if dynamic url not exits. Right now I am getting this error for invalid url . I also set
if (res != 200) {
return {
notFound: true,
};
}
but didn't work. here is my full code:
export async function getStaticPaths() {
const search_url = "https://backendapi.mydomain.com/blog-list/";
const search_result = await fetch(search_url);
const search_data = await search_result.json(search_result);
const paths = search_data.map((data) => {
return {
params: {
id: data.blog_slug,
},
};
});
return {
paths,
fallback: true, // false or 'blocking'
};
}
export async function getStaticProps(context) {
const id = context.params.id;
const res = await fetch(`https://mydomain/blog-api/${id}`);
const Blog = await res.json();
const search_url = "https:/mydomain/blog-list/";
const search_result = await fetch(search_url);
const search_data = await search_result.json(search_result);
if (res != 200) {
return {
notFound: true,
};
}
return {
props: {
data: Blog,
search_data: search_data,
},
};
}
here is my jsx look like:
function blog_details({ data, search_data }) {
return (<h1>data.blog_slug<h1/)
}
How can I generate a different title on every page within a sub-directory?
My code throws no errors, but unfortunately the Title component renders every title-item on every page that it is supposed to, e.g. every app.com/title/<title> renders the same view ( a list of titles). I think that getStaticPaths is correctly parameterised, but I don't think that getStaticProps is.
export default function Title({ paper }) {
// paper is just the entire dataset, and isn't split by id or author etc.
return (
<div>
{paper.map(paper => (
<h1>{paper.data.title}</h1>
))}
</div>
)
}
export async function getStaticProps({ params }) {
// ideally, results should be split down to e.g. `/api/getPapers/title`, but this didn't work
const results = await fetch(`http://localhost:3000/api/getPapers/`).then(res => res.json());
return {
props: {
paper: results
}
}
}
export async function getStaticPaths() {
const papers = await fetch('http://localhost:3000/api/getPapers').then(res => res.json());
const paths = papers.map(paper => {
return {
params: {
authors: paper.data.title.toLowerCase().replace(/ /g, '-')
}
}
})
return {
paths,
fallback: false
}
}
This is the getPapers API function.
const faunadb = require("faunadb");
// your secret hash
const secret = process.env.FAUNADB_SECRET_KEY;
const q = faunadb.query;
const client = new faunadb.Client({ secret });
module.exports = async (req, res) => {
try {
const dbs = await client.query(
q.Map(
// iterate each item in result
q.Paginate(
// make paginatable
q.Match(
// query index
q.Index("all_research_papers") // specify source
)
),
(ref) => q.Get(ref) // lookup each result by its reference
)
);
// ok
res.status(200).json(dbs.data);
} catch (e) {
// something went wrong
res.status(500).json({ error: e.message });
}
};
My attempts to render a separate page for each document were missing a dynamic API call. I was simply hoping to render dynamic pages with a single (batched-document) API call.
Here is a typical dynamic API route called [index.js]:
const faunadb = require('faunadb')
// your secret hash
const secret = process.env.FAUNADB_SECRET_KEY
const q = faunadb.query
const client = new faunadb.Client({ secret })
export default async (req, res) => {
const {
query: { index },
} = req;
try {
const papers = await client.query(
q.Get(q.Ref(q.Collection('<name of the collection>'), index))
);
res.status(200).json(papers.data);
} catch (e) {
res.status(500).json({ error: e.message });
}
};
Once your data is being retrieved dynamically, you can set up a dynamic page route, e.g. [id].js, that fetches the data using useSWR.
const { data, error } = useSWR(`/api/getPapers/${id}`, fetcher);
This is an example fetcher function:
const fetcher = (url) => fetch(url).then((r) => r.json());
In my case, I could then retrieve any given field using the call {data.<field>}.
You are returning authors in your Path object. You will need to make sure that your page file is named with authors included. For example:
app_directory
|- pages
|- home.js
|- title
|- [authors].js
Perhaps where you say authors in your params object, you do mean title. In which case, rename the params object and page filename.
const paths = papers.map(paper => {
return {
params: {
title: paper.data.title.toLowerCase().replace(/ /g, '-')
}
}
})
app_directory
|- pages
|- home.js
|- title
|- [title].js
Here are the docs for getStaticPaths(). https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation
EDIT:
Since your API function returns the Page from your query, the shape of the result will likely be
{
before: [/* before cursor */],
after: [/* after cursor */],
data: [
{ /* paper Document */ },
{ /* paper Document */ },
{ /* paper Document */ },
]
}
In which case, your code will need to map over papers.data not on papers itself.
const paths = papers.data // select the data
.map(paper => {
return {
params: {
title: paper.data.title.toLowerCase().replace(/ /g, '-')
}
}
})
I have a page with a list of objects called stories that displays all my stories in an array. I also have a detail page with displays an individual story.
I want to click on a link on any given story on the list, then it will navigate me to the individual story. I want to use _id as my dynamic part of the URL, as shown in the GraphQL below.
My Graphql
export const listAllStories = () => {
const query = gql`
query StoryEntries($size: Int) {
storyEntries(_size: $size) {
data {
_id
_ts
name
premises{
data{
_id
content
}
}
createdAt
}
}
}
`
return graphQLClient
.request(query, { size: 999 })
.then(({ storyEntries: { data } }) => data)
}
IN MY PAGES API I HAVE
export default async function handler(req, res) {
const handlers = {
GET: async () => {
const storyEntries = await listAllStories()
res.json(storyEntries)
},
}
if (!handlers[req.method]) {
return res.status(405).end()
}
await handlers[req.method]()
}
ON THE STORY LIST PAGE I HAVE
const ENTRIES_PATH = '/api/entries/allStories'
const useEntriesFlow = ({ initialEntries }) => {
const { data: entries } = useSWR(ENTRIES_PATH, {
initialData: initialEntries,
})
const EntryItem = ({ entry }) => (
<>
{entries?.map((entry) => (
{entry.name}
<Link href="/story/[storyId]" as={`/story/${entry._id}`}>
<a>Go</a>
</Link>
))}
</>
)
export const getStaticProps = async () => ({
props: {
initialEntries: await listAllStories(),
},
revalidate: 1,
})
This is fine and works.
**AND THEN ON THE DETAIL PAGE FOR EACH INDIVIDUAL STORY [storyId].js I HAVE **
export default function Story({story}) {
const router = useRouter()
const storyId = router.query.storyId
return(
<>
<h5>hello {story._id}</h5>
</>
)
}
export const getStaticPaths = async () => {
const res = await fetch(`${server}/api/entries/allStories/`);
const { data } = await res.json();
const paths = data.map(story => {
return {
params: { id: story._id.toString() }
}
// trying to get the _id from each story
})
return {
paths,
fallback: false
}
}
export const getStaticProps = async (context) => {
const { storyId } = context.query; // Your dynamic page is [storyId].js
const server = "http://localhost:3000";
const res = await fetch(`${server}/api/entries/allStories/${storyId}`);
// trying to get the params._id from each story
console.log(res)
const { data } = await res.json();
return {
props: { story: data }
}
}
ERROR
TypeError: Cannot read properties of undefined (reading 'map')
QUESTION
All I want to do is click on any story link, then it takes me to the details page, via the _id. I have tried a few things but I'm doing something (or some things) wrong.
Any help will be greatly appreciated.
EDIT AFTER. ERROR I'M GETTING. I'm not able to map my results on getStaticPaths
export const getStaticProps = async (context) => {
const { storyId } = context.query; // Your dynamic page is [storyId].js
const server = "YOUR SERVER VARIABLE";
const res = await fetch(`${server}/api/entries/allStories/${storyId}`);
// trying to get the params._id from each story
const { data } = await res.json();
return {
props: { story: data }
}
}
uncomment
const router = useRouter()
const storyId = router.query.storyId
// some helpful links
// https://nextjs.org/docs/basic-features/data-fetching#the-paths-key-required
// https://stackoverflow.com/questions/65783199/error-getstaticpaths-is-required-for-dynamic-ssg-pages-and-is-missing-for-xxx
export const getStaticPaths = async () => {
const server = "http://localhost:3000";
const data = await fetch(`${server}/api/entries/allStories/`).then(res => res.json() )
const paths = data.map(({_id}) => ({
params: { storyId: _id },
}))
return {
paths,
fallback: false
}
}
export const getStaticProps = async (context) => {
const storyId = context.params.storyId; // Your dynamic page is [storyId].js
const server = "http://localhost:3000";
// const res = await fetch(`${server}/api/entries/allStories/${storyId}`);
// trying to get the params._id from each story
// single api call (here)
const res = await fetch(`${server}/api/entries/allStories/`);
// removing const { data } because the data will be returned when calling res.json()
const data = await res.json();
// instead of the calling the single api (just a fix not recommended to access [0] directly )
return {
props: { story: data.filter(story => story._id === storyId)[0] }
}
}