Storefront Oluşturma
Storefront mimarisi
Özel bir storefront için önerilen mimari: sunucu tarafında API anahtarı yönetimi, bir Next.js proxy katmanı, önbellekleme, hata eşleme ve idempotency.
Markdown olarak görüntüleBorkol storefront API'si, mağazadaki her siparişi okuyabilen ve her sepeti değiştirebilen tek bir tenant API anahtarıyla doğrulanır. Bu tek gerçek mimariyi belirler: anahtar sizin kontrolünüzdeki bir sunucuda yaşar ve tarayıcı yalnızca o sunucuyla konuşur. Bu sayfa, Next.js'i işlenmiş örnek olarak kullanarak önerilen yapıyı anlatır; aynı katmanlama, sunucu tarafında render edilen veya backend-for-frontend tarzındaki her stack için geçerlidir.
Anahtar sunucu tarafında kalır
API anahtarını asla tarayıcıya, bir mobil binary'ye veya istemciye teslim edilen herhangi bir bundle'a göndermeyin. Somut olarak:
commerce:writeile anahtarı elinde tutan herkes mağazadaki her sepeti değiştirebilir ve sepetleri siparişe dönüştürebilir.GET /v1/api/commerce/orders/{id}müşteri sahipliği kontrolü yapmaz; anahtar tek başına yetkilendirir. Sızan bir anahtar her siparişin e-postasını ve adresini açığa çıkarır.- Rate limit (dakikada 120 request) ziyaretçi başına değil, mağaza başınadır. Tarayıcıdaki bir anahtar, tek bir ziyaretçinin tüm mağazanın bütçesini tüketmesine izin verir.
Next.js açısından: anahtar, NEXT_PUBLIC_ öneki olmayan bir ortam değişkeninde yaşar ve yalnızca route handler'larda, server component'lerde ve server action'larda okunur. Kesin bir garanti isterseniz, CI sırasında istemci bundle'larınızı anahtar öneki için grep'leyin.
Müşteri oturum token'ı (X-Customer-Token) da bir bearer kimlik bilgisidir. Onu sunucu tarafı bir oturumda veya proxy'nizin okuduğu bir httpOnly cookie'de tutun; istemci tarafı JavaScript'e vermeyin.
Proxy deseni
Tarayıcıya kendi uygulamanız tarafından sunulan küçük, storefront'a göre şekillendirilmiş bir API verin ve Borkol'u o katman çağırsın:
Browser ── /api/* (your app) ── https://api.borkol.com/v1/api/*
|
adds Authorization: Bearer {key}
adds X-Customer-Token from the visitor's session
enforces YOUR ownership rules
maps Borkol errors to UI-safe shapesMinimal bir Next.js route handler çifti:
import { cookies } from "next/headers";
const BASE = process.env.BORKOL_API_URL; // https://api.borkol.com
const KEY = process.env.BORKOL_API_KEY;
async function borkol(path, init = {}) {
const jar = await cookies();
const customerToken = jar.get("customer_token")?.value;
return fetch(`${BASE}${path}`, {
...init,
headers: {
Authorization: `Bearer ${KEY}`,
"Content-Type": "application/json",
...(customerToken ? { "X-Customer-Token": customerToken } : {}),
...init.headers,
},
cache: "no-store",
});
}
export async function POST() {
const res = await borkol("/v1/api/commerce/carts", { method: "POST" });
const body = await res.json();
const jar = await cookies();
jar.set("cart_id", body.data.id, { httpOnly: true, sameSite: "lax", path: "/" });
return Response.json(body, { status: res.status });
}
export async function GET() {
const jar = await cookies();
const cartId = jar.get("cart_id")?.value;
if (!cartId) return Response.json({ data: null }, { status: 200 });
const res = await borkol(`/v1/api/commerce/carts/${cartId}`);
return Response.json(await res.json(), { status: res.status });
}Proxy katmanının kuralları:
- Sepet id'si ziyaretçinin tanıtıcısıdır. Onu proxy'nizin ayarladığı bir
httpOnlycookie'de saklayın; istemcinin gönderdiği bir sepet id'sine asla güvenmeyin. Bir sepet id'sini bilen herkes proxy'niz üzerinden o sepeti okuyabilir ve değiştirebilir, bu yüzden onu bir oturum id'si gibi ele alın. - Sipariş sahipliğini kendiniz uygulayın.
GET /v1/api/commerce/orders/{id}çağrısını proxy'lemeden önce, sipariş id'sinin bu ziyaretçinin kendi checkout'undan üretildiğini doğrulayın (örneğin checkout sırasında sipariş id'sini oturumda saklayın). Giriş yapmış müşteriler için Borkol'un token'a göre sizin adınıza kapsamladığıGET /v1/api/commerce/my/ordersendpoint'ini tercih edin. - Genel bir passthrough kurmayın.
/v1/apialtındaki herhangi bir yolu anahtarınız ekli olarak ileten bir route, sızan anahtar problemini bir adım öteye taşıyarak yeniden yaratır. Yalnızca arayüzünüzün ihtiyaç duyduğu operasyonları, mümkün olan en dar girdilerle açığa çıkarın. X-Customer-Token'ı sunucu tarafında ekleyin: sepet, checkout, teklif talebi ve müşteri endpoint'lerinde kendi oturumunuzdan. Bu, istemci token'ı hiç görmeden, giriş yapmış ziyaretçilere her yerde grup fiyatlandırmasını verir.
Önbellekleme: katalog evet, sepetler asla
API, önbelleklenebilir okumalar ile ziyaretçiye özel durum olarak temiz biçimde ikiye ayrılır.
Serbestçe önbellekleyin (paylaşımlı, saniyeler ile dakikalar arası):
GET /v1/api/commerce/categories: ağaç nadiren değişir. Dakikalarca TTL sorun değildir.- Misafir trafiği için
GET /v1/api/commerce/productsveGET /v1/api/commerce/products/{id}: URL başına önbellekleyin (filter[category]vepagedahil). Bu aynı zamanda dakikada 120 request limitine karşı ana kaldıracınızdır; önbellekten sunulan yoğun bir katalog sayfası sıfır bütçe harcar. GET /v1/api/commerce/configurator/products/{id}/schema: ürün başına dakikalarca önbellekleyin; satıcılar configurator'ları seyrek düzenler.
Next.js'te katalog sayfalarında fetch revalidation (next: { revalidate: 60 }) veya ISR kullanın ve bir stock.adjusted olayı geldiğinde webhook alıcınızdan revalidateTag ile geçersiz kılın.
Asla önbelleklemeyin veya yalnızca ziyaretçi başına önbellekleyin:
X-Customer-Tokentaşıyan herhangi bir request. Ürün response'ları müşteri bağlamına göre fiyatlanır (grup fiyatları, KDV hariç gösterim, misafirlerden gizlenen fiyatlar), bu yüzden paylaşımlı bir önbellek bir müşterinin fiyatlandırmasını başka birine sızdırır. Ya giriş yapmış trafik için önbelleklemeyi atlayın ya da önbelleği müşteri grubuna göre anahtarlayın.- Sepetler, checkout, bir sepetin kargo yöntemleri, teklif endpoint'leri,
my/orders: her zamancache: "no-store". Sepet response'ları toplamlar ve politika için tek doğru kaynaktır; bayat bir sepet yanlış fiyatlar gösterir. POST /v1/api/commerce/configurator/products/{id}/quote: ucuzdur ve seçime bağlıdır; en fazla bir oturum içinde tam request body'si başına memoize edin.
Stok gösterimi: stock_status önbelleklenmiş ürün response'larına gömülüdür, dolayısıyla önbellekle birlikte bayatlar. Bu kabul edilebilir, çünkü checkout stoğu yetkili biçimde yeniden kontrol eder ve bir şey tükenmişse yapılandırılmış bir 409 döner; katalogtaki stok rozetlerini bilgilendirme amaçlı sayın.
Hata yönetimi ve idempotency
Borkol hata envelope'larını bir kez, proxy'de, arayüzünüzün switch'leyebileceği şekillere eşleyin. Eksiksiz katalog Hatalar ve rate limit'ler sayfasındadır; storefront'unuzun mesaja değil koda göre işlemesi gerekenler şunlardır:
| Durum | Ayırt edici | Storefront tepkisi |
|---|---|---|
| 401 | message: "Unauthenticated." | Anahtarınız bozuk veya iptal edilmiş. Operasyonel alarm, kullanıcı hatası değil. |
| 401 | message: "Invalid customer token." | Müşteri oturumunun süresi dolmuş. Oturumu temizleyin, ziyaretçiden tekrar giriş yapmasını isteyin. |
| 403 | code: "tenant_suspended" | Mağaza genelinde kesinti durumu. Bakım sayfası gösterin. |
| 403 | error: "Module not enabled: ..." | Yanlış yapılandırma (anahtar veya mağaza). Operasyonel alarm. |
| 403 | message: "This cart belongs to a customer." | Sepet bir hesaba bağlı. Giriş isteyin, ardından token ile yeniden deneyin. |
| 409 | code: "QUOTE_REQUIRED" | Teklif talebi akışına yönlendirin. |
| 409 | message: "Cart contents changed." | Sepeti hata body'sindeki cart nesnesinden yeniden oluşturun; out_of_stock ve discount_unavailable alanlarını gösterin; alıcının yeniden denemesine izin verin. |
| 409 | code: "QUOTE_NOT_OFFERED" / "OFFER_CHANGED" / "OUT_OF_STOCK" | Teklif token sayfası durumları: süresi dolmuş/geri çekilmiş, değişmiş (satıcıdan yeniden göndermesini isteyin) veya stok yetersizliği. |
| 422 | code mevcut (login_required, guest_only, b2b_approval_required, b2c_only, min_order_value, cart_constraint) | Sepet politikası ihlalleri: her biri belirli bir arayüz durumuna eşlenir. |
| 422 | errors nesnesi | Alan doğrulama. Alan başına mesajları gösterin; API'nin mesajları kullanıcıya gösterilmeye hazırdır. |
| 429 | message: "Too Many Attempts." | Retry-After header'ına uyun. Geri çekilin; yeniden deneme fırtınası yaratmayın. |
Idempotency ve yeniden denemeler:
- Checkout yeniden denenebilir. Zaten dönüştürülmüş bir sepet için checkout'a tekrar POST atmak mevcut siparişi 200 ile döner. Checkout sırasında bir ağ timeout'unda hata göstermek yerine aynı request'i yeniden deneyin; yinelenen bir sipariş oluşturamazsınız.
- Teklif kabulü de aynı şekilde idempotent'tir: zaten kabul edilmiş bir teklifi kabul etmek aynı
order_iddeğerini döner. - Sepet satırı ekleme idempotent DEĞİLDİR. Başarılı bir
POST .../linesçağrısını yeniden denemek miktarları birleştirir (2, 4 olur). Satır mutasyonlarını yalnızca hiçbir response gelmeyen ağ seviyesindeki hatalarda otomatik yeniden deneyin ve körlemesine tekrar POST atmak yerine sepeti yeniden çekip beklenen durumla karşılaştırarak uzlaştırın. - Okumalar her zaman güvenle yeniden denenebilir: 429 ve 5xx durumlarında üstel geri çekilme ile.
- Rate limit'i bütçeleyin: tüm mağaza için dakikada 120 request, katalog okumalarını önbelleklemek, hiçbir şeyi render başına fan-out'lara dönüştürmemek ve sipariş durumunu webhook'ların size zaten söyleyeceğinden daha hızlı sorgulamamak demektir.
Ortam değişkenleri
Anahtarı istemci bundle'ından uzak tutan ve ortamları birbirinden ayrılabilir kılan kurallar:
# Server-only: no NEXT_PUBLIC_ prefix, ever, on these three.
BORKOL_API_URL=https://api.borkol.com
BORKOL_API_KEY=1|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
BORKOL_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Client-safe values carry the prefix and contain no secrets.
NEXT_PUBLIC_STORE_NAME="Four Seasons Garden"- Ortam başına bir anahtar. Dağıtımına göre adlandırılmış ayrı anahtarlar oluşturun (
storefront-prod,storefront-staging); böylece her biri bağımsız iptal edilebilir ve dashboard'dakilast_used_athangisinin canlı olduğunu söyler. - Anahtarları yüzey başına minimal kapsamlayın. Build zamanında katalog önceden render eden bir araç yalnızca
commerce:readister; checkout sunucusunun yazma anahtarını yeniden kullanmak yerine ona kendi salt okunur anahtarını verin. - Webhook secret'ı endpoint başınadır ve API anahtarı gibi oluşturma sırasında bir kez gösterilir. Her ikisini de repository'de değil, secret yöneticinizde saklayın.
- Anahtar rotasyonu: yenisini oluştur, dağıt, eskisini iptal et. Anahtarların süresi dolmaz, dolayısıyla rotasyon disiplini size aittir.