Careeroza
Fetching with fetch
fetch in Server Components
Next.js uses the standard Web fetch. On the server you can add Next.js options: cache and next: { revalidate, tags }. In Next.js 15+, fetch is not cached by default; you opt in.
// lib/api.ts
export type Product = { id: number; name: string; price: number; category: string };
const API = process.env.API_URL ?? 'http://localhost:8080';
export async function getProduct(id: number): Promise<Product | null> {
const res = await fetch(API + '/api/products/' + id, {
next: { revalidate: 60, tags: ['product-' + id] },
});
if (res.status === 404) return null;
if (!res.ok) throw new Error('Product API failed: ' + res.status);
return (await res.json()) as Product;
}Typing and Checking the Response
res.json() returns any. Cast it to your type, or better, check it at runtime (see the TypeScript material, Type-safe Patterns). A small guard:
Example
type Product = { id: number; name: string; price: number };
function isProduct(x: unknown): x is Product {
if (typeof x !== 'object' || x === null) return false;
const o = x as Record<string, unknown>;
return typeof o.id === 'number' && typeof o.name === 'string' && typeof o.price === 'number';
}
async function main() {
// Response exists in Node 18+, so we can build one locally (no network)
const ok = new Response(JSON.stringify({ id: 1, name: 'Clean Code', price: 450 }));
const bad = new Response(JSON.stringify({ id: '1', title: 'oops' }));
for (const res of [ok, bad]) {
const data: unknown = await res.json();
console.log(isProduct(data) ? 'valid: ' + data.name : 'invalid payload: ' + JSON.stringify(data));
}
}
main();Output
valid: Clean Code
invalid payload: {"id":"1","title":"oops"}