add_action('rest_api_init', function () {
register_rest_field('lookbook', 'custom_data', [
'get_callback' => function ($post_arr) {
$post_id = $post_arr['id'];
$raw_image = get_field('hero_image', $post_id);
return [
'subtitle' => get_field('subtitle', $post_id) ?: '',
'hero' => $raw_image ? [
'url' => $raw_image['sizes']['large'] ?? $raw_image['url'],
'alt' => $raw_image['alt'] ?: get_the_title($post_id),
] : null,
'linked_products' => array_map(function ($prod_id) {
return [
'id' => $prod_id,
'title' => get_the_title($prod_id),
'price' => get_post_meta($prod_id, '_price', true),
];
}, get_field('featured_products', $post_id) ?: []),
];
},
'schema' => null,
]);
});Dacă ai legat vreodată un frontend de Next.js la un WordPress headless cu ACF, știi sentimentul: dai un fetch pe un Custom Post Type și primești un JSON de 400KB plin de metadate WordPress de care nu are nimeni nevoie. Am pățit asta anul trecut la un magazin WooCommerce hibrid cu peste 12.000 de produse și articole de tip "Lookbook" legate prin ACF.
Problema nu e WordPress-ul în sine, ci faptul că majoritatea devilor trântesc plugin-ul ACF to REST API și se miră de ce TTFB-ul pe frontend trece de 800ms. Plugin-ul ăla aruncă absolut tot în răspuns, inclusiv configurări interne de câmpuri, chei _field_12345 și relații circulare nerezolvate.
REST API: register_rest_field bate orice plugin automat
Când folosești REST clasic, cel mai curat mod este să declari CPT-ul cu 'show_in_rest' => true și să expui doar datele strict necesare prin register_rest_field(). Nu vrei să lași ACF să polueze root-ul obiectului.
La proiectul menționat, după ce am rescris endpoint-urile cu register_rest_field și am extras doar câmpurile de care avea nevoie componenta de React, payload-ul pe un grid de 24 de carduri a scăzut de la 380KB la doar 22KB. Asta înseamnă parsing JSON aproape instant pe telefoane mid-range și costuri de transfer reduse.
Secretul e să nu iei get_fields($post->ID) orbește. Folosește get_field('nume_camp', $post->ID) targetat și formatează datele înainte să plece din PHP: dacă ai o imagine, trimite doar { id, url, alt, width, height }, nu tot array-ul gigant de 40 de proprietăți pe care ți-l dă ACF by default.
Când merită WPGraphQL cu WPGraphQL for ACF
GraphQL e o binecuvântare pentru frontend devs datorită tipizării automate cu GraphQL Code Generator. Dacă ai TypeScript pe frontend, WPGraphQL cu extensia de ACF îți generează tipurile fără efort.
Totuși, există un trade-off pe care nu ți-l spune nimeni: WPGraphQL mănâncă mult mai mult CPU și memorie pe server. La o interogare complexă cu 3 nivele de nested repeaters și post relationships, PHP-ul stă de 3-4 ori mai mult să rezolve arborele de GraphQL decât ar sta pe un endpoint REST optimizat cu un transient cache.
Regula mea simplă după zeci de mii de request-uri în producție:
- Dacă am conținut static sau semi-dinamic (articole, landing pages, lookbook-uri) și pot pune un Edge Cache agresiv (Cloudflare sau Varnish) în fața WP-ului: REST API cu serialize manual e imbatabil ca viteză.
- Dacă construiesc un dashboard complex sau un app mobil unde frontend-ul cere combinații imprevizibile de câmpuri: WPGraphQL, dar neapărat cu plugin-ul de WPGraphQL Smart Cache activat.
Ce faci cu relațiile bidirecționale
Dacă ai un CPT "Proiecte" legat de produse WooCommerce prin ACF Post Object, ai grijă la bucle infinite de serializare. În REST, returnează doar ID-urile sau datele minimale ale produsului (id, title, price, slug). Nu include obiectul complet de WooCommerce cu toate variantele lui în interiorul CPT-ului, altfel transformi o interogare banală într-un coșmar de zeci de query-uri SQL în spate.
Voi ce stack folosiți pentru headless WordPress în 2024? Ați rămas pe REST cu endpoint-uri custom sau ați trecut complet pe WPGraphQL?