[Next.js 15] - Part 8. How Data Fetching Works in Next.js

Ace Lennox
Data fetching in Next.js 15 introduces powerful techniques to handle data securely and efficiently, including caching and revalidating. Let’s break it down into best practices and practical steps.
Best Practices for Data Fetching in Next.js 15
Fetch Data on the Server Whenever Possible
Fetching data on the server provides key advantages:
Direct access to data sources (e.g., databases) without intermediaries.
Improved security, as you avoid exposing API keys or tokens in the client.
Reduced latency: Server rendering fetches and renders data in the same environment, avoiding unnecessary round trips.
Better user experience: No need for loading spinners during the initial render.
Diagram of Server vs. Client Data Fetching:
Client Fetching: Browser → API Route → Database → API Route → Browser (adds latency).
Server Fetching: Server → Database → Server → Browser (faster, more efficient).
By default, Next.js 15 renders pages on the server, so take advantage of this by fetching data server-side during the initial render.
Where to Fetch Data
Fetch Data Where It’s Needed:
Avoid fetching global data in top-level components unless necessary. Fetching data closer to the components that use it keeps the application modular and avoids unnecessary re-fetching.Caching in Fetch:
Next.js 15 enhances the built-infetch
function to utilize caching effectively, making subsequent requests faster and seamless.
Streaming
Streaming allows React and Next.js 15 to:
Render part of the page immediately (e.g., navigation, layout).
Defer rendering of data-dependent components while they fetch required data.
This approach improves user experience by ensuring visible progress and interactive elements even when data is still loading.
Step-by-Step Implementation
1. Server-Side Data Fetching with getServerSideProps
Fetch data on the server to pre-render pages with the required content. Example:
export async function getServerSideProps(context) {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return {
props: { data },
};
}
export default function Page({ data }) {
return <div>Data: {JSON.stringify(data)}</div>;
}
2. Static Data Fetching with getStaticProps
For content that rarely changes, use static generation:
export async function getStaticProps() {
const res = await fetch('https://api.example.com/static-data');
const data = await res.json();
return {
props: { data },
};
}
export default function StaticPage({ data }) {
return <div>Static Data: {JSON.stringify(data)}</div>;
}
3. Client-Side Fetching with useEffect
Use this for interactive features or when data is fetched post-initial render:
import { useEffect, useState } from 'react';
export default function ClientPage() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then((res) => res.json())
.then((data) => setData(data));
}, []);
return <div>Client Data: {data ? JSON.stringify(data) : 'Loading...'}</div>;
}
4. Streaming in Layouts
Enable streaming in React Server Components for complex layouts:
Render layout immediately.
Defer rendering data-dependent components with
Suspense
.
import React, { Suspense } from 'react';
function DataComponent() {
// Simulate fetching data
const data = fetchData();
return <div>Fetched Data: {data}</div>;
}
export default function Layout() {
return (
<div>
<h1>Streaming Layout</h1>
<Suspense fallback={<div>Loading Data...</div>}>
<DataComponent />
</Suspense>
</div>
);
}
Why Next.js 15 Excels in Data Fetching
Built-in caching for
fetch
ensures efficient API calls.Streaming support enhances rendering for modern user experiences.
Server-first approach aligns perfectly with secure, fast, and scalable applications.
By following these practices and examples, you can leverage Next.js 15 to create performant, dynamic, and user-friendly applications.