Main idea
In App Router, pages and layouts are Server Components by default. That means you can fetch data directly in the component before it renders.
Use a route handler only when the browser or another service needs an API. For a normal page read, fetch on the server.
Why server fetch
A hackathon judge does not care where your data comes from. They care that the deployed page opens fast, shows real content, and does not leak secrets.
Server-side fetching helps because your database query, API key, and filtering logic stay on the server. They are not shipped in the client JavaScript bundle.
Choose one data source
Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.
0 of 4 done
Server page fetch
This example creates a projects page that fetches data on the server. The page component is async, then it awaits fetch and renders the result.
Replace the URL with your real API later. The shape of the data must match what you render.
// app/projects/page.tsx
type Project = {
id: number
name: string
city: string
}
async function getProjects() {
const res = await fetch('https://example.com/api/projects')
if (!res.ok) {
throw new Error('Failed to load projects')
}
return res.json() as Promise<Project[]>
}
export default async function ProjectsPage() {
const projects = await getProjects()
return (
<main className="p-6">
<h1 className="text-2xl font-bold">Student projects</h1>
<ul className="mt-4 space-y-3">
{projects.map((project) => (
<li key={project.id} className="rounded border p-3">
<p className="font-medium">{project.name}</p>
<p className="text-sm text-gray-600">{project.city}</p>
</li>
))}
</ul>
</main>
)
}Ask AI well
You can ask ChatGPT or Gemini to draft the fetch function and TypeScript type. Give it one sample JSON item and the page path.
Check three things in the answer: no 'use client' at the top, the component is async, and errors are handled when the response is not ok.
Common wrong turn
Many older tutorials fetch page data with useEffect and useState. That makes the browser wait, then fetch, then update the page.
For App Router, start with server fetching. Move to a Client Component only when the user must click, type, filter, or use a browser API.
Caching note
Current Next.js docs say fetch requests are not cached by default. A fetch can block rendering until it finishes.
For this lesson, keep it simple. Get the data rendering correctly first. Add caching or streaming later only when your app needs it.
Do this now
Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.
0 of 5 done