Appearance
Data fetching and cache (practical, not mystical)
Next has opinions about caching. They've also changed over time. Treat this as a mental model - verify against the docs for your Next version when you ship for real.
Fetch on the server (happy path)
tsx
async function getUser(id: string) {
const res = await fetch(`https://api.example.com/users/${id}`, {
next: { revalidate: 300 }, // seconds
})
if (!res.ok) throw new Error('Failed to load user')
return res.json()
}
export default async function UserPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const user = await getUser(id)
return <h1>{user.name}</h1>
}Comments for the code above:
- In recent Next versions,
paramsmay be a Promise -awaitit revalidate: 300≈ ISR-ish: serve cached, refresh in the background after 5 minutes- Errors can bubble to
error.tsx
Dynamic always (no static cache)
tsx
fetch(url, { cache: 'no-store' })Use when the data must be fresh every request (dashboards, per-user private data). Cost: less caching benefit.
Route Handlers (API routes)
ts
// app/api/hello/route.ts
import { NextResponse } from 'next/server'
export async function GET() {
return NextResponse.json({ ok: true })
}≈ Nuxt server routes / Nitro handlers. Good for webhooks, BFF endpoints, form posts.
Mutations from the client
Two common paths:
- Server Actions - functions you can call from forms / client, run on server
- Call your Route Handler / external API from a Client Component (TanStack Query still useful here)
Sketch of a Server Action:
tsx
// app/actions.ts
'use server'
export async function createPost(formData: FormData) {
const title = String(formData.get('title') ?? '')
// write to DB…
}tsx
// in a Server Component page
<form action={createPost}>
<input name="title" />
<button type="submit">Create</button>
</form>As you can see - progressive enhancement vibes. For rich client UIs, you'll still want Query or local state around the action call.
What I'd put where
| Need | Approach |
|---|---|
| Public blog listing | RSC + fetch + revalidate |
| User settings editor | Client island + mutation (action or API) |
| Infinite scroll feed | Client + TanStack Query |
| Webhook | Route Handler |
Honest warning
Caching semantics have been a moving target across Next majors. When something "mysteriously" stays stale - check cache, revalidate, and whether you're in a static or dynamic render path. I've lost evenings to that. 😅
Next: routing UI - links, loading, errors.
Enjoy!