add_action('rest_api_init', function () {
register_rest_route('app/v1', '/produse/', [
'methods' => 'GET',
'callback' => 'get_clean_products_data',
'permission_callback' => '__return_true',
]);
});
function get_clean_products_data($request) {
$args = [
'post_type' => 'produs_custom',
'posts_per_page' => 20,
'post_status' => 'publish'
];
$posts = get_posts($args);
$data = [];
foreach ($posts as $post) {
// Extrag doar datele ACF strict necesare pentru cardul din frontend
$specs = get_field('specificatii_tehnice', $post->ID);
$data[] = [
'id' => $post->ID,
'title' => $post->post_title,
'slug' => $post->post_name,
'sku' => $specs['sku_intern'] ?? null,
'pret' => $specs['pret_promo'] ?? $specs['pret_standard'],
'brand' => isset($specs['brand']) ? $specs['brand']->post_title : null,
];
}
return new WP_REST_Response($data, 200);
}Acum câteva luni am lucrat la migrarea unui catalog e-commerce cu peste 12k produse și specificații complexe. Proiectul folosea WordPress pe backend, cu Custom Post Types (CPT) și zeci de câmpuri din Advanced Custom Fields (ACF), iar frontend-ul era construit pe Next.js. Dacă ai încercat vreodată să legi combinația asta direct la REST API-ul din WP, știi deja cât de haotic arată răspunsul JSON nativ.
Am avut un șoc când am făcut primul fetch pe o pagină de categorie: răspunsul cântărea 4.2MB. Era plin de URL-uri de imagini pe care nu le foloseam, chei de _links, date despre autor, versiuni și metadate interne ale ACF-ului. Frontend-ul se mișca greu, iar build-ul de SSG pe Vercel dura o veșnicie. Trebuia să curățăm mizeria asta repede.
Soluția 1: Curățarea prin REST API nativ
Dacă ești blocat pe REST și nu vrei să adaugi arhitectură nouă, prima greșeală e să bifezi doar Show in REST pe CPT și ACF. Asta îți aruncă absolut totul în răspuns. Ca să scazi drastic dimensiunea payload-ului, soluția scurtă e să creezi endpoints custom cu register_rest_route sau să interceptezi răspunsul prin rest_prepare_{post_type}.
În loc să trimiți tot obiectul generat de ACF cu get_fields(), extragi doar valorile de care ai nevoie. Dacă ai un câmp de tip Relationship, ACF îți dă nativ tot obiectul Post asociat. În endpoint-ul tău curat, extragi doar ID, title și slug. Doar din trucul ăsta simplu am scăzut dimensiunea payload-ului cu vreo 60%.
Soluția 2: WPGraphQL și plugin-ul WPGraphQL for ACF
Sincer, după ce am optimizat REST API-ul la un nivel acceptabil, am decis totuși să testăm WPGraphQL. Diferența a fost de la cer la pământ. GraphQL rezolvă din fașă problema de over-fetching. Frontend-ul cere exact câmpurile de care are nevoie pentru o anumită componentă, nimic mai mult.
Când instalezi wp-graphql și wp-graphql-acf, fiecare grup de câmpuri ACF primește o opțiune numită "Show in GraphQL" și un nume de GraphQL Field. În loc de endpoint-uri babane de JSON, trimiți query-uri structurate. Răspunsul pentru aceeași pagină de listare de 12k produse a scăzut de la 4.2MB la 180KB. Build time-ul în Next.js a scăzut cu 35% imediat.
Trade-off-uri reale: Ce alegi în producție?
Nu există glonț de argint aici, ambele opțiuni vin cu minusuri pe care le-am simțit pe pielea noastră:
- REST API custom: E excelent dacă ai un proiect mic sau mediu și vrei zero dependențe exotice. Codul se scrie repede în
functions.phpsau într-un plugin dedicat. Minusul? Trebuie să scrii manual maparea datelor pentru fiecare endpoint nou și pierzi timp la mentenanță când se schimbă structura de ACF. - WPGraphQL: E soluția ideală pentru e-commerce și cataloage mari. Query-urile sunt rapide, iar DX-ul (Developer Experience) pe frontend în Next/Nuxt e genial. Minusul? Adaugi cel puțin două plugin-uri grele în WP, iar dacă ai servere slabe de hosting (shared de 5 euro), query-urile GraphQL complexe pot mânca multă memorie PHP la început până intră cache-ul (WP GraphQL Smart Cache e obligatoriu).
Noi am rămas pe WPGraphQL pentru proiectul respectiv și nu ne-am uitat înapoi. Voi ce soluție folosiți când aveți de expus CPT-uri complexe și ACF în headless: endpoints custom pe REST sau treceți direct pe GraphQL?