# Next.js

> Next.js App Router kullanan bir mağazada SSR, script ve oturum açmış müşteri entegrasyonu.

Kaynak: /tr/integrate/nextjs/

Bu sayfa, [SSR](/tr/integrate/server-rendering.md) yönteminin Next.js App Router'daki uygulamasını anlatır. Ek paket kurulumu gerekmez; `next` ve `react` yeterlidir.

## Ortam değişkenleri

```ini
NEXT_PUBLIC_SAYPROOF_BASE=https://api.sayproof.example
NEXT_PUBLIC_SAYPROOF_SITE_KEY=SITE-ANAHTARINIZ
NEXT_PUBLIC_SAYPROOF_SCRIPT=https://cdn.sayproof.example/sayproof.js
SAYPROOF_SSR_KEY=SUNUCU-ANAHTARINIZ
SAYPROOF_CHANNEL=web
```

- `NEXT_PUBLIC_` önekli değerler tarayıcıya aktarılır; site anahtarı ve adresler gizli değildir.
- Sunucu anahtarı `SAYPROOF_SSR_KEY` **öneksiz** tanımlanır ve tarayıcıya aktarılmamalıdır.
- Sunucunuz Sayproof'a farklı bir adres üzerinden erişiyorsa (örneğin iç ağ) ayrıca `SAYPROOF_BASE` değişkenini tanımlayın.

## Sunucudan istek

Sunucu anahtarını okuyan kodu ayrı bir dosyada tutun ve dosyanın başına `import "server-only"` satırını ekleyin. Bir client component bu dosyayı içe aktarmaya çalışırsa build başarısız olur; böylece anahtar yanlışlıkla tarayıcı bundle'ına girmez.

```ts
import "server-only";

const base = process.env.SAYPROOF_BASE || process.env.NEXT_PUBLIC_SAYPROOF_BASE;

export async function sayproofReviews(product: string, name: string, locale: string) {
  const query = new URLSearchParams({
    product,
    name,
    lang: locale,
    channel: process.env.SAYPROOF_CHANNEL ?? "web",
  });
  try {
    const answer = await fetch(`${base}/v1/ssr?${query}`, {
      headers: { Authorization: `Bearer ${process.env.SAYPROOF_SSR_KEY}` },
      next: { revalidate: 60 },
      signal: AbortSignal.timeout(1500),
    });
    return answer.ok ? await answer.json() : null;
  } catch {
    return null;
  }
}
```

## Ürün sayfası

```tsx
import Script from "next/script";
import { sayproofReviews } from "@/lib/sayproof.server";

export default async function ProductPage({ params }) {
  const product = await getProduct(params.slug);
  const reviews = await sayproofReviews(product.sku, product.name, params.locale);

  return (
    <>
      <div
        key={product.sku}
        data-review-mount
        data-review-product={product.sku}
        data-review-product-name={product.name}
        data-review-locale={params.locale}
        suppressHydrationWarning
        dangerouslySetInnerHTML={{ __html: reviews?.html ?? "" }}
      />
      <Script
        id="sayproof"
        type="module"
        strategy="afterInteractive"
        crossOrigin="anonymous"
        src={process.env.NEXT_PUBLIC_SAYPROOF_SCRIPT}
        data-review-site-key={process.env.NEXT_PUBLIC_SAYPROOF_SITE_KEY}
        data-review-base={process.env.NEXT_PUBLIC_SAYPROOF_BASE}
        data-review-locale={params.locale}
      />
    </>
  );
}
```

Dikkat edilmesi gerekenler:

- **`key={product.sku}`:** Varyant değiştiğinde React container'ı yeniden oluşturur; script yeni container'ı algılar ve bağlar.
- **`suppressHydrationWarning`:** Container'ın içeriği script tarafından yönetildiği için React'in içerik uyuşmazlığı uyarısını kapatır.
- **`dangerouslySetInnerHTML`:** Sunucudan gelen `html` burada olduğu gibi kullanılır; ikinci bir sanitizer'dan geçirilmemelidir.
- `router.refresh()` sonrasında React container'ı yeni HTML ile yeniden oluşturursa script yeni içeriği algılar ve yeniden devralır.
- Ürün grubunun tüm yorumlarını göstermek için grup kodunu kullanın, container'a `data-review-scope="group"` attribute'unu ekleyin ve sunucudan gönderilen isteğe de `scope: "group"` parametresini ekleyin.

## Yapılandırılmış veri

Ürün sayfanız zaten bir `Product` JSON-LD'si içeriyorsa ikinci bir `Product` eklemeyin; puanı ve yorumları kendi nesnenize ekleyin. Escape kuralı ve örnek için: [SSR › Yapılandırılmış veri](/tr/integrate/server-rendering.md#yapilandirilmis-veri-json-ld).

```tsx
const jsonLd = reviews?.json_ld?.aggregateRating
  ? { ...productJsonLd, aggregateRating: reviews.json_ld.aggregateRating, review: reviews.json_ld.review ?? [] }
  : productJsonLd;

<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: jsonLdScript(jsonLd) }} />
```

## İçerik güvenliği politikası (CSP)

Nonce kullanıyorsanız `<Script>` bileşenine `nonce` değerini verin. Script, eklediği `<style>` öğelerine (`#sayproof-style`, `#sayproof-style-form`, `#sayproof-style-lightbox`) de aynı nonce değerini yazar.

## Oturum açmış müşteri

Oturum açmış müşteri için formu sipariş numarası sormadan açmak üzere bir Route Handler yazılır ve container'a `data-review-viewer="pending"` attribute'u eklenir. Yöntemin tamamı ve Next.js örneği için: [Oturum açmış müşteri](/tr/integrate/signed-in-customers.md).

## Sipariş detayı sayfası

Hesap bölümündeki sipariş detayı sayfasında siparişin ürünlerini sayfanın içinde değerlendirtmek için sunucu bileşeninde oturumdaki e-posta adresi ve sipariş numarasıyla Sayproof'a sorun, yanıtı `data-review-order` öğesinin içine yazın. `sayproofOrderState` fonksiyonu ve yanıtın değerleri için: [Oturum açmış müşteri › Sipariş detayı sayfası](/tr/integrate/signed-in-customers.md#siparis-detayi-sayfasi). Oturum bilgisini kendi kimlik doğrulama kütüphanenizden alın; buradaki `auth()` yalnızca örnektir.

```tsx
import Script from "next/script";
import { auth } from "@/auth";
import { sayproofOrderState } from "@/lib/sayproof-order.server";

export default async function OrderPage({ params }) {
  const session = await auth();
  const state = await sayproofOrderState(params.id, session?.user?.email, params.locale);

  return (
    <>
      {state ? (
        <div
          key={params.id}
          data-review-order={params.id}
          suppressHydrationWarning
          dangerouslySetInnerHTML={{ __html: state }}
        />
      ) : null}
      <Script
        id="sayproof"
        type="module"
        strategy="afterInteractive"
        crossOrigin="anonymous"
        src={process.env.NEXT_PUBLIC_SAYPROOF_SCRIPT}
        data-review-site-key={process.env.NEXT_PUBLIC_SAYPROOF_SITE_KEY}
        data-review-base={process.env.NEXT_PUBLIC_SAYPROOF_BASE}
        data-review-locale={params.locale}
      />
    </>
  );
}
```

Yanıt kişiye özeldir ve `cache: "no-store"` ile alınır; sayfanın bu bölümü önbelleğe alınmaz. Yanıtta ürün yoksa öğe hiç eklenmez.

## Headless kullanım

Yorum listesini kendi bileşenlerinizle gösterecekseniz container'a `data-review-mode="headless"` attribute'unu ekleyin ve `html` yerine `state_script` değerini kullanın. Örnek için: [Headless kullanım](/tr/integrate/headless.md).

## Hazır kit

Sayproof ekibi, bu sayfadaki yöntemleri hazır bileşenler olarak içeren bir Next.js kiti sağlar: sunucudan istek ve JSON-LD birleştirme, varsayılan bileşen için container, headless kullanım için container, script etiketi, oturum açmış müşteri route'u, yorum listeleme, oylama ve bildirim client'ı ile sipariş detayı sayfası için Sayproof isteği (`fetchSayproofOrder()`), yanıtı güvenle yazan etiket (`sayproofOrderStateScript()`) ve bileşen (`<SayproofOrderReviews>`). `fetchSayproofOrder()` yanıtta ürün yoksa ya da istek başarısız olursa `null` döndürür; `<SayproofOrderReviews>` bu durumda hiçbir şey çizmez. Kit mağaza reposuna kopyalanır; paket kurulumu gerektirmez ve es5 hedefli TypeScript ayarlarıyla derlenir. Kitin kullanılması zorunlu değildir; bu sayfadaki kod aynı işi görür.
