Ana içeriğe geç
Ahmet Faruk UzunkayaYaratıcı geliştirici
← Projelere dön
Web ürünüYayındaGüncellendi · 1 Ekim 2026

KapıdaMart

Arka ucu aynı Next.js uygulamasının içinde çalışan bir demo pazaryeri.

Next.js 16 (App Router) ile yazdığım bir e-ticaret vitrini. REST uçları uygulamanın kendi içinden sunuluyor, veri de bellekte duruyor; yani ayrı bir sunucuya ya da veritabanına ihtiyaç yok.

Next.js 16TypeScriptZustand
Durum
Yayında
Kategori
Web ürünü
Güncelleme
1 Ekim 2026
Yığın
Next.js 16TypeScriptZustand
KapıdaMart

KapıdaMart, Trendyol ya da Hepsiburada tarzında bir demo pazaryeri vitrini. 17 kategorideki 408 ürün arasında arama yapabilir, sonuçları fiyata, kategoriye ve sıralamaya göre filtreleyebilirsin; öne çıkan ve indirimli ürünlere, aktif kampanyalara ve yorumlara da göz atabilirsin. Kayıt olunca gerisi de açılıyor: sepet, sipariş, adres, kayıtlı ödeme yöntemi, favori, bildirim ve hesap ayarları akışlarının hepsi çalışıyor. Verdiğin bir siparişi iptal edebilir ya da iade talebi açabilirsin.

Next.js 16 (App Router), React 19 ve TypeScript ile yazdım. İstemcide oturum Zustand'da, sepet TanStack Query ile tutuluyor. Arka ucu ayrı bir servis olarak kurmadım, aynı Next.js uygulamasının içinde çalıştırdım. Bu yüzden bütün site, veritabanı olmadan tek bir dağıtım birimi olarak ve output: "standalone" ile tek bir Docker imajında yayınlanıyor.

Bütün API tek bir route dosyasında

Bütün REST uçları src/app/api/[...slug]/route.ts adlı tek bir catch-all route'tan sunuluyor. Dosya GET, POST, PUT ve DELETE için tek bir handler dışa aktarıyor. Handler gelen yol parçalarını "product/:id" gibi desenlerle sırayla karşılaştırıyor; isteği ilk eşleşen desen karşılıyor, hiçbiri eşleşmezse NOT_FOUND dönüyor. Dosyanın üst kısmındaki uçlar herkese açık. Ondan sonraki her uç, Authorization: Bearer başlığındaki token'dan oturum sahibini buluyor; bulamazsa 401 dönüyor. Kullanıcıya ait kaynaklarda URL'deki userId oturum sahibiyle eşleşmezse de 403 dönüyor.

Token'ı JWT yerine opak bir değer olarak tuttum. Girişte üretiliyor, istemcide localStorage'da saklanıyor ve her istekte başlıkta gönderiliyor; sunucu 401 döndürürse istemci oturumu kendiliğinden kapatıyor. Gövdedeki alanlar sunucuda doğrulanıp kırpılıyor: e-posta biçimi, en az 6 karakterlik şifre, 1-5 arası puan, pozitif tamsayı adet, 12-19 haneli kart numarası, 1-12 arası son kullanma ayı gibi. Hatalar VALIDATION_ERROR koduyla 400 olarak dönüyor. Bütün yanıtlar tek bir yardımcıdan, şu iki biçimden biriyle çıkıyor:

{ "success": true, "data": {} }
{ "success": false, "code": "PRODUCT_NOT_FOUND", "message": "..." }

Bellekte bir mağaza

Veri src/server/ altında, sürecin belleğinde duruyor. Katalog 17 kategori × 24 ürün olarak deterministik üretiliyor, yani ürünler, kampanyalar ve yorumlar her açılışta aynı çıkıyor. İndirimli satış fiyatını ürün üretilirken bir kez hesaplıyorum; sepet, sipariş, filtreler ve SEO hep bu alanı kullanıyor. Kullanıcılar, oturumlar, sepetler, siparişler ve adresler de aynı bellekte tutuluyor.

Siparişi istemcinin gönderdiği listeden değil, sunucudaki sepetten oluşturuyorum. Idempotency-Key başlığı aynı siparişin iki kez oluşmasını engelliyor; iptal edilen bir siparişin stoğu da geri dönüyor. Sipariş durumları ise demo için ayrılmış bir uçla elle ilerletiliyor.

Ana sayfa ve ürün sayfaları sunucuda veriyle render ediliyor. Ürün sayfaları derleme sırasında statik üretiliyor, stok bilgisi ise istemcide canlı veriyle tazeleniyor. Sitemap, robots ve Open Graph görselleri de uygulamanın kendi rotalarından geliyor.

Bu kurgunun bir bedeli de var: her şey bellekte olduğu için sunucu yeniden başladığında kullanıcılar, sepetler ve siparişler sıfırlanıyor.