Setting export for metadata for dynamically routed page
Answered
Spectacled bear posted this in #help-forum
Spectacled bearOP
This might be a stupid question but I'm having a lot of trouble with it. I'm using app router structure and am trying to set export metadata dynamically for blog post pages. route is posts/[slug]. My page.tsx looks something like what is copied below. How do I setup export const metadata and have it use the same data as has been fetched from fetchPost. Ideally fetchPost is called once and the response is shared between export const metadata and the default export rather than them each having to call it. Appreciate any help!
const fetchPost = async (slug: string) => {
const { data, error } = await supabase
.from('posts')
.select('*, posttags(tag_id, tags(id, name))')
.eq('slug', slug)
.single();
const postWithTags: Post = {
...data,
tags: data.posttags.map((pt: { tags: Tag }) => pt.tags),
};
return {
post: postWithTags,
error: null,
};
};
interface PostPageProps { params: Promise<{slug:string}>; }
export default async function PostPage({ params }: PostPageProps) {
const slug = (await params).slug;
const { post, error } = await fetchPost(slug);
if (!post) {
return <div>Loading...</div>;
}
return (
<h1 className="text-2xl lg:text-4xl font-bold text-center pt-2">{post.title}</h1>
<p className="text-black text-sm italic mr-4 pt-2 pl-6">{post.date}</p>
<div className="relative h-auto mb-4">
<Image
src={post.cover_photo}
alt={post.title}
layout="responsive"
width={800}
height={400}
className="w-full"
loading="eager"
priority
/>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
);
}Answered by B33fb0n3
@Spectacled bear yea, as luis said, you can use the react cache. Wrap your functions inside this "per request" cache and your database will only be queried once.
Great to see, that the generateMetadata function worked perfectly fine for this
Great to see, that the generateMetadata function worked perfectly fine for this
6 Replies
@Spectacled bear This might be a stupid question but I'm having a lot of trouble with it. I'm using app router structure and am trying to set export metadata dynamically for blog post pages. route is posts/[slug]. My page.tsx looks something like what is copied below. How do I setup export const metadata and have it use the same data as has been fetched from fetchPost. Ideally fetchPost is called once and the response is shared between export const metadata and the default export rather than them each having to call it. Appreciate any help!
`const fetchPost = async (slug: string) => {
const { data, error } = await supabase
.from('posts')
.select('*, posttags(tag_id, tags(id, name))')
.eq('slug', slug)
.single();
const postWithTags: Post = {
...data,
tags: data.posttags.map((pt: { tags: Tag }) => pt.tags),
};
return {
post: postWithTags,
error: null,
};
};
interface PostPageProps { params: Promise<{slug:string}>; }
export default async function PostPage({ params }: PostPageProps) {
const slug = (await params).slug;
const { post, error } = await fetchPost(slug);
if (!post) {
return <div>Loading...</div>;
}
return (
<h1 className="text-2xl lg:text-4xl font-bold text-center pt-2">{post.title}</h1>
<p className="text-black text-sm italic mr-4 pt-2 pl-6">{post.date}</p>
<div className="relative h-auto mb-4">
<Image
src={post.cover_photo}
alt={post.title}
layout="responsive"
width={800}
height={400}
className="w-full"
loading="eager"
priority
/>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
);
}`
you can [use the generateMetadata function](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) to generate dynamic metadata:
export async function generateMetadata({ params }) {
const post = await getPost(...);
const title = posts.title;
...
return {
title: '...',
}
}@B33fb0n3 you can [use the generateMetadata function](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) to generate dynamic metadata:
tsx
export async function generateMetadata({ params }) {
const post = await getPost(...);
const title = posts.title;
...
return {
title: '...',
}
}
Spectacled bearOP
Thanks for the guidance. That works fine but the issue is I also have my default export PostPage which is going to call getPost(...); This means that getPost is called twice and so the database is going to be queried twice when I only need it queried once. Is there a way to do it while only querying the database once?
You can wrap your getPost() in react “cache” function and it returns a memoized version of your fn, it will only hit the server once, the second time will pull the data from the cache
@Spectacled bear Thanks for the guidance. That works fine but the issue is I also have my default export PostPage which is going to call getPost(...); This means that getPost is called twice and so the database is going to be queried twice when I only need it queried once. Is there a way to do it while only querying the database once?
const fetchPost = cache( async () => {….} )
Then you call fetchPost() in both the component and generateMetadata and it’ll make 1 query for the metadata, then the component will use the result from the cache.
Btw this cache lives per request, which means that if you request the page again, it’ll execute again, but just once instead of twice per request
Then you call fetchPost() in both the component and generateMetadata and it’ll make 1 query for the metadata, then the component will use the result from the cache.
Btw this cache lives per request, which means that if you request the page again, it’ll execute again, but just once instead of twice per request
@Spectacled bear yea, as luis said, you can use the react cache. Wrap your functions inside this "per request" cache and your database will only be queried once.
Great to see, that the generateMetadata function worked perfectly fine for this
Great to see, that the generateMetadata function worked perfectly fine for this
Answer