import { cache } from 'react';
import { Metadata } from 'next';
import { db } from '@/lib/db';
// Deduplicare per-request pentru ORM
const getProduct = cache(async (id: string) => {
return await db.query.products.findFirst({ where: (p, { eq }) => eq(p.id, id) });
});
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
const { id } = await params;
const product = await getProduct(id);
if (!product) return { title: 'Produs negăsit' };
return {
title: `${product.name} | Magazin Auto`,
description: product.summary,
openGraph: {
title: product.name,
description: product.summary,
},
};
}Dacă lucrezi cu App Router de ceva vreme, știi că generarea de metadata pare simplă la prima vedere, dar devine rapid un coșmar dacă nu ești atent la caching. În Next.js 16, mecanismul din spatele generateMetadata e matur, însă capcanele clasice au rămas: fetch-uri duplicate prin ORM, imagini OG lente și scheme markup aruncate la întâmplare.
Capcana fetch-urilor duplicate și salvarea cu React cache()
La un magazin online de piese auto cu vreo 24k de pagini indexate, aveam o problemă ciudată la lansare: TTFB-ul pe paginile de produs era dublu față de ce testasem pe mock-uri.
De ce? Făceam fetch la produs o dată în generateMetadata pentru a scoate titlul și descrierea dinamică, și încă o dată în Server Component-ul paginii pentru a randa detaliile efective. Next.js deduplică automat apelurile făcute prin fetch() nativ, dar dacă tragi date direct prin Drizzle sau Prisma către o bază de date PostgreSQL, acel monkey-patch nu se aplică. Rezultatul? Două query-uri identice de câte 35-45ms pe fiecare request.
Soluția e simplă, dar mulți o ratează: funcția cache() din pachetul react. Împachetezi apelul la baza de date într-un memo per-request, iar query-ul rulează o singură dată pe durata ciclului de render.
Trade-off-ul cu OG Images dinamice: Edge vs Node
Pentru generarea de imagini pe social share, convenția opengraph-image.tsx alături de ImageResponse este impecabilă din punct de vedere DX. Randezi un JSX simplu cu Flexbox și primești un PNG generat direct la server.
Aici apare un trade-off sincer. Dacă configurezi ruta să ruleze pe Edge runtime, ai un cold-start minuscul, undeva sub 50ms, dar ești limitat la fonturi TTF/WOFF curate și nu ai acces la librării complete de Node. Dacă lași ruta pe runtime-ul de Node clasic, ai libertate totală, dar pe containere mici sau instanțe serverless poți atinge lejer latențe de 600-800ms când un crawler (Facebook sau Twitter) lovește link-ul prima oară.
Pentru proiectul cu piese auto, am rămas pe Edge, combinat cu un header agresiv de cache: imaginile se generează o dată și rămân agățate pe CDN până facem deploy nou.
Structured Data (JSON-LD) fără dependențe externe
Nu instala librării terțe de tip next-seo în App Router. Sunt un rest din epoca Pages Router și nu fac altceva decât să adauge greutate inutilă în node_modules.
Pentru JSON-LD, recomandarea oficială și cea mai stabilă este injectarea unui tag <script type="application/ld+json"> direct în componenta de pagină. Dacă vrei siguranță la tipizare ca să nu ratezi câmpuri cerute de Google (cum ar fi prețul sau disponibilitatea stocului), instalezi schema-dts ca devDependency pentru autocompletion, apoi serializezi obiectul nativ cu JSON.stringify.
Voi cum gestionați generarea de OG images pe proiectele mari — mergeți pe ImageResponse intern sau delegați totul către un serviciu extern dedicat?