**page.jsx **
"use client"
import React, { useState, useEffect } from 'react';
import PostCard from '@/components/PostCard/page';
import { BASE_API_URL } from '@/utils/constants';
const Home = () => {
const [blogs, setBlogs] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
const fetchPosts = async () =>
{
setIsLoading(true);
try {
const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_API_URL}/api/posts`,{
cache:"no-store",
});
if (!res.ok) {
throw new Error('Failed to fetch posts');
}
const data = await res.json();
setBlogs(data);
} catch (error) {
setError(error);
} finally {
setIsLoading(false);
}
};
fetchPosts();
}, []);
return (
<div className='container mx-auto border-rose-500 flex flex-col gap-4 flex-wrap justify-center w-5/6 md:w-3/6'>
<div className='flex flex-col'>
<div>
<h1 className='text-3xl md:text-5xl font-bold mb-8 text-white'>My Blogs</h1>
</div>
{isLoading && <div className="loader">Loading...</div>}
{error && <div className="error">{error.message}</div>}
{blogs.length > 0 && (
<div className="flex flex-col gap-2 mb-8 w-full">
{blogs.map((post) => (
<PostCard data={post} key={post._id} />
))}
</div>
)}
{!isLoading && !error && blogs.length === 0 && (
<div className="empty-state">No posts found.</div>
)}
</div>
</div>
);
};
export default Home;
postDesc/[slug].jsx:
import { BASE_API_URL } from "@/utils/constants";
import React from "react";
import Markdown from 'react-markdown';
import Image from 'next/image';
async function getPostById(slug) {
const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_API_URL}/api/posts/${slug}`, {
cache: "no-store",
});
if (!res.ok) {
throw new Error("Fetching Post Failed!");
}
return res.json();
}
const PostDetail = async ({ params }) => {
const post = await getPostById(params.slug);
const formatDateString = (dateString) => {
const dateObject = new Date(dateString);
const options = { day: "2-digit", month: "long", year: "2-digit" };
return dateObject.toLocaleDateString("en-GB", options).replace(/\//g, " ");
};
const handleImageTag = (domNode) => {
if (domNode.type === 'tag' && domNode.name === 'img') {
// Customize styling for the img tag
return <Image {...domNode.attribs} alt="img" className="w-full h-80 object-center my-4" width={0}
height={0}
sizes="100vw"
style={{ width: '100%', height: "320px" }} />;
}
return null;
};
const options = {
replace: handleImageTag,
};
return (
<div class="max-w-screen-lg mx-auto mb-8 md:w-3/6">
<main>
<div class="mb-4 md:mb-0 w-full mx-auto relative">
<Image
alt="img"
src={post.img}
width={0}
height={0}
sizes="100vw"
style={{ width: '100%', height: "320px" }}
className="transition-opacity ease-in duration-700 opacity-80 w-full h-96 object-cover lg:rounded p-4 md:p-0"
/>
</div>
<div className="flex flex-col items-center mt-8">
<h2 class="text-3xl text-center md:text-normal md:text-5xl font-semibold text-white leading-tight mb-4 px-2">
{post.title}
</h2>
<div className="flex items-center gap-4 m-2">
<div>
<Image
alt="img"
src={post.img}
width={0}
height={0}
sizes="100vw"
style={{ width: '30px', height: "30px" }}// Choose the appropriate layout based on your needs
className="object-cover rounded p-4 md:p-0"
/>
</div>
<p className="text-sm">{post.author}</p>
<p className="text-txt text-sm">
{formatDateString(post.createdAt)}
</p>
</div>
</div>
<div></div>
<div className="content mx-auto flex flex-col lg:flex-row md:mt-2 flex-wrap md:p-4 w-fit">
<div className="px-4 lg:px-0 text-white flex-wrap text-lg leading-relaxed w-full text-justify text-txt tracking-tight font-primary">
{
<Markdown>{post.content}</Markdown>
}
</div>
</div>
</main>
</div>
);
};
export default PostDetail;
and I have two api routes
- /api/posts
import connection from '@/utils/dbConnect';
import Post from '@/models/Posts';
import { NextResponse } from "next/server";
export const GET = async(request)=>
{
try{
await connection();
const posts = await Post.find()
return new NextResponse(JSON.stringify(posts), {status: 200, headers: { 'Cache-Control': 'no-store' }});
}catch(error){
console.log(error);
return new NextResponse("Database Error", {status: 400, headers: { 'Cache-Control': 'no-store' }});
}
}
- /api/posts/[slug]
import connection from '@/utils/dbConnect';
import Post from '@/models/Posts';
import { NextResponse } from "next/server"
export const GET = async(request, {params})=>{
const {slug} = params
// fetch data
try{
await connection();
const post = await Post.findOne({slug: slug});
return new NextResponse(JSON.stringify(post), {status: 200, headers: { 'Cache-Control': 'no-store' }})
}
catch(err){
return new NextResponse("Database Error", {status: 500, headers: { 'Cache-Control': 'no-store' }})
}
}
I have these two url that i use:
NEXT_PUBLIC_BASE_API_URL=https://app-name.vercel.app (this does not fetch new posts)
NEXT_PUBLIC_BASE_API_URL=http://localhost:3000
When I use my localhost URL in API to hit the database and get the posts, it returns all the new posts and updates, but when I use the deployed URL (the .vercel one), I get the cached data, and no new data is fetched.
I think you already recognized the problem. Vercel uses caching for API requests, so to get new blog posts, you will have to wait until the cache is invalidated (depending on how much time you set for it).
On
localhost, the data is never cached, so you will always get the latest blog posts.If you want to fetch the new blog posts when they're updated immediately, you can set the revalidation duration to
0like this:See this doc to learn more: