İçeriğe geç

Server-side rendering (SSR)

Markdown

Yorumların sayfanın ilk yanıtında, arama motorlarının okuyabileceği biçimde yer alması.

Server-side rendering (SSR) kullanıldığında mağazanızın sunucusu, sayfayı oluştururken yorumların hazır HTML’ini Sayproof’tan alır ve sayfaya ekler. Tarayıcı sayfayı açtığında yorumlar sayfada zaten bulunur; script yüklendiğinde aynı içeriği yeni bir ağ isteği yapmadan devralır ve etkileşimi başlatır.

Sağladığı yararlar:

  • Yorum metni sayfa kaynağında yer alır; arama motorları ve bağlantı önizlemeleri içeriği script’i beklemeden görür.
  • Sayfa, ekrandaki değerlerle birebir aynı Review ve AggregateRating yapılandırılmış verisini (JSON-LD) içerir.
  • Script yüklenene kadar yorum bölümü boş kalmaz ve sayfa düzeni kaymaz. HTML, yorum yazma alanını da içerir: alan data-review-gate-state="pending" durumunda ve düğmesi pasif olarak gelir, script devraldığında etkinleşir. Yıldız satırları, filtreler, arama, sıralama, oy düğmeleri ve fotoğraflar da script devraldığında çalışır; “Devamını oku” düğmesi gizli gelir; script metnin yüksekliğini ölçer ve metin 4 satırı aşıyorsa düğmeyi gösterir.

Bu yöntem her programlama dilinde ve framework’te uygulanabilir; sunucunuzun bir HTTP isteği yapıp dönen HTML’i sayfaya ekleyebilmesi yeterlidir. Next.js için ayrı bir sayfa bulunur: Next.js.

  • Bir sunucu anahtarı (panelde Kanallar › Anahtarlar). Sipariş gönderen sisteminizin kullandığı anahtarla aynı anahtar olabilir. Anahtar yalnızca sunucuda tutulur. Bkz. Kanal, alan adı ve anahtarlar.
  • Kanal kodu; örneğin web.
  • Sayfada Sayproof script’i. SSR ile hazırlanan HTML’i etkileşimli hâle getiren script’tir.
GET https://api.sayproof.example/v1/ssr?product=URUN-KODU&channel=web&lang=tr&name=Ürün%20adı
Authorization: Bearer SUNUCU-ANAHTARINIZ
Parametre Zorunlu Açıklama
product Evet Ürün kodu (en fazla 128 karakter); container’daki data-review-product değeriyle aynı.
channel Evet Kanal kodu.
scope Hayır variant (varsayılan) ya da group.
lang Hayır tr ya da en. Belirtilmezse ya da desteklenmiyorsa kanalın dili kullanılır.
name Hayır Ürün adı. Yapılandırılmış veride ürün adı olarak kullanılır.
sort Hayır Pazarın izin verdiği sıralamalardan biri. Belirtilmezse varsayılan sıralama (tarih) kullanılır. İzin verilmeyen bir değer 400 yanıtı alır.
style Hayır cards ya da sidebar: HTML’in hangi düzenle hazırlanacağı. Belirtilmezse kanalın panelde seçilen düzeni kullanılır. Container’da data-review-style kullanıyorsanız aynı değeri gönderin.
theme Hayır warm, trust, editorial, minimal, soft, bold, natural ya da night: HTML’in hangi hazır tasarımla hazırlanacağı. Belirtilmezse kanalın panelde seçilen tasarımı kullanılır. Container’da data-review-theme kullanıyorsanız aynı değeri gönderin.
{
"html": "<section data-review-root data-review-ssr …>…</section>",
"state": { "reviews": [], "aggregate": {}, "core": {}, "words": {} },
"state_script": "<script type=\"application/json\" data-review-state>…</script>",
"components": [
{ "name": "summary", "where": "server" },
{ "name": "list", "where": "server" },
{ "name": "form", "where": "client" }
],
"json_ld": { "@type": "Product", "aggregateRating": {}, "review": [] },
"json_ld_script": "{…}",
"total": 200,
"version": "3",
"etag": "W/\"…\"",
"stylesheet": "https://api.sayproof.example/storefront/sayproof.css"
}
Alan Kullanımı
html Container’ın içine olduğu gibi eklenecek HTML. Sunucu tarafında escape edilmiş ve stil kapsamı tanımlanmıştır. Yapısı script’in oluşturduğu bileşenle aynıdır; öğeler için: DOM attribute’ları.
state Aynı içeriğin veri hâli: yorumlar, özet, sıralama, zorunlu öğeler ve arayüz metinleri. Alanlar yorum listesi yanıtıyla aynıdır.
state_script Yalnızca state verisini taşıyan etiket. Headless kullanımda html yerine bu etiket kullanılır.
components Bileşenler ve nerede oluşturuldukları. where değeri server olanlar (özet ve yorum listesi) html içinde hazır gelir; client olan yorum formu sunucuda oluşturulmaz, müşteri formu açtığında tarayıcıdaki script tarafından oluşturulur. Bu yüzden SSR kullanılan sayfaya da script etiketi eklenir; eklenmezse yorumlar görünür ama form açılmaz.
json_ld Yapılandırılmış veri nesnesi.
json_ld_script Aynı nesnenin <script> etiketi içine güvenle yazılabilecek hâli.
stylesheet Bileşenin temel stillerini taşıyan CSS dosyasının adresi. html içindeki kök öğenin (data-review-root) ilk alt öğesi bu dosyaya giden bir <link rel="stylesheet" data-review-stylesheet> etiketidir; böylece yorumlar script yüklenmeden biçimli görünür. İsterseniz aynı adresi sayfanın <head> bölümüne de ekleyebilirsiniz.

html alanını container’ın içine ekleyin ve sayfaya script’i ekleyin. Yanıt alınamadıysa container boş bırakılır:

const container = `<div data-review-mount
data-review-product="URUN-KODU"
data-review-product-name="Ürün adı">${reviews?.html ?? ""}</div>`;
const script = `<script type="module" src="https://cdn.sayproof.example/sayproof.js"
data-review-site-key="SITE-ANAHTARINIZ"
data-review-base="https://api.sayproof.example"
data-review-locale="tr"></script>`;
  • Sayfanızda ürün için bir Product JSON-LD’si yoksa json_ld_script değerini olduğu gibi ekleyin:

    <script type="application/ld+json">…json_ld_script…</script>
  • Sayfanızda zaten bir Product JSON-LD’si varsa ikinci bir Product eklemeyin. json_ld.aggregateRating ve json_ld.review alanlarını kendi nesnenize ekleyin.

Nesneyi kendiniz metne dönüştürüyorsanız <, >, &, U+2028 ve U+2029 karakterlerini \uXXXX biçiminde escape edin: örneğin < karakteri \u003c olur. Yorum metinleri müşteriler tarafından yazılır; düz JSON.stringify sonucu, </script> içeren bir yorum nedeniyle sayfanızı bozabilir.

function jsonLdScript(value) {
return JSON.stringify(value).replace(/[<>&\u2028\u2029]/g, (character) =>
`\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`
);
}
const product = { "@context": "https://schema.org", "@type": "Product", name, sku };
if (reviews?.json_ld?.aggregateRating) {
product.aggregateRating = reviews.json_ld.aggregateRating;
product.review = reviews.json_ld.review ?? [];
}
const markup = `<script type="application/ld+json">${jsonLdScript(product)}</script>`;

Arama motorları ve yapay zekâ arama araçları sayfanın sunucudan gelen HTML’ini okur; betikle sonradan çizilen yorumları her zaman görmez. Sunucuda hazırlanan yorumlar sayfanın ilk HTML’inde yer alır:

  • Satın alması doğrulanan yorumda Doğrulanmış alıcı ibaresi görünür metindir; yalnız simge ya da renk değildir.
  • JSON-LD yalnız schema.org’un Review, Rating ve AggregateRating alanlarını kullanır. Satın alma doğrulaması gibi schema.org’da karşılığı olmayan bir bilgi yapılandırılmış veriye eklenmez, görünür metinde kalır.
  • Diğer pazarlardan gelen yorumlar listede kaynak pazarıyla görünür, ama review dizisine ve aggregateRating değerine girmez.

Sayproof’a gönderilen istek sayfanızın açılmasını hiçbir zaman geciktirmemelidir.

  • İsteğe kısa bir zaman aşımı verin; 1,5 saniye önerilir.
  • Zaman aşımında ya da 200 dışındaki yanıtlarda container’ı boş bırakın. Bu durumda script yorumları tarayıcıda kendisi getirir.
Yanıt Anlamı
403 refused / scope_missing Anahtarın sayfayı sunucuda hazırlama izni yok (panelden oluşturulan sunucu anahtarlarında bu izin her zaman vardır). Panelden yeni bir sunucu anahtarı oluşturun.
403 refused / unknown_channel Kanal kodu yanlış ya da kanal kapalı.
409 refused / no_site_key Kanalın kullanımda bir site anahtarı yok ve kanalın yöntem beyanı için kendi sayfanız bağlı değil; yöntem beyanı bağlantısı oluşturulamadığı için yanıt verilmez.
400 invalid / product Ürün kodu eksik ya da çok uzun.
400 sort_not_allowed Sıralama, pazarın izin verilen listesinde değil.
400 invalid / style Düzen cards ya da sidebar değil.
400 invalid / theme Tasarım warm, trust, editorial, minimal, soft, bold, natural ya da night değil.
429 rate_limited Anahtar başına 5 dakikada 6.000 istek sınırı aşıldı.

Sunucu anahtarıyla gönderilen isteğin yanıtı Cache-Control: private, max-age=60 ve ETag header’larını taşır; aynı endpoint’e tarayıcıdan site anahtarıyla istek gönderilirse Cache-Control: public, max-age=60 döner. İsteğe son alınan etag değeriyle If-None-Match header’ı eklenirse, içerik değişmediğinde body içermeyen bir 304 yanıtı döner. Kendi tarafınızda en fazla 60 saniyelik önbellek kullanabilirsiniz; yeni yayınlanan bir yorum en geç bu süre sonunda sayfada görünür.

Tüm örnekler aynı işi yapar: isteği zaman aşımıyla gönderir ve hata durumunda null döndürür.

export async function sayproofReviews({ product, name, language }) {
const query = new URLSearchParams({
product,
name,
lang: language,
channel: process.env.SAYPROOF_CHANNEL,
});
try {
const answer = await fetch(`${process.env.SAYPROOF_BASE}/v1/ssr?${query}`, {
headers: { Authorization: `Bearer ${process.env.SAYPROOF_SSR_KEY}` },
signal: AbortSignal.timeout(1500),
});
return answer.ok ? await answer.json() : null;
} catch {
return null;
}
}
import os
import requests
def sayproof_reviews(product, name, language):
try:
answer = requests.get(
f"{os.environ['SAYPROOF_BASE']}/v1/ssr",
params={
"product": product,
"name": name,
"lang": language,
"channel": os.environ["SAYPROOF_CHANNEL"],
},
headers={"Authorization": f"Bearer {os.environ['SAYPROOF_SSR_KEY']}"},
timeout=1.5,
)
except requests.RequestException:
return None
return answer.json() if answer.ok else None

Jinja ya da Django template’inde HTML’i escape etmeden ekleyin:

<div data-review-mount data-review-product="{{ product.sku }}" data-review-product-name="{{ product.name }}">
{% if reviews %}{{ reviews.html|safe }}{% endif %}
</div>
{% if reviews %}<script type="application/ld+json">{{ reviews.json_ld_script|safe }}</script>{% endif %}
function sayproof_reviews(string $product, string $name, string $language): ?array
{
$query = http_build_query([
'product' => $product,
'name' => $name,
'lang' => $language,
'channel' => getenv('SAYPROOF_CHANNEL'),
]);
$curl = curl_init(getenv('SAYPROOF_BASE') . '/v1/ssr?' . $query);
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT_MS => 1500,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('SAYPROOF_SSR_KEY')],
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
curl_close($curl);
return ($body !== false && $status === 200) ? json_decode($body, true) : null;
}

Kullanıcı sayfada başka bir varyant seçtiğinde container’ın data-review-product değerini güncelleyin; script yeni ürünün yorumlarını kendisi getirir. Sayfa sunucudan yeniden yükleniyorsa (örneğin her varyantın ayrı bir adresi varsa) yeni html gelir ve script bu içeriği devralır.