Next.js ile İkas Headless E-Ticaret: GraphQL API ile Özel Mağaza Kurulumu
İkas GraphQL API ile Next.js headless mağaza kurun: OAuth 2.0 token akışı, ürün/sipariş query'leri, SSG ile ürün sayfaları, ISR ve webhook entegrasyonu. Türk geliştiriciler için pratik rehber.

Önemli Not: Bu yazıdaki teknik bilgiler yazım tarihi itibarıyla geçerlidir. Kullanılan kütüphaneler, API'ler ve servisler zaman içinde değişebilir. Ücretlendirme, yasal düzenleme ve vergi konularında ilgili resmi kaynakları ve uzmanları referans alınız. Bu içerik bilgilendirme amaçlı olup herhangi bir finansal veya hukuki tavsiye niteliği taşımamaktadır.
Türkiye'de e-ticaret geliştiriyorsanız muhtemelen İkas'ı duymuşsunuzdur: 2026 itibarıyla 15.000'den fazla aktif mağazayla Türkiye'nin en hızlı büyüyen e-ticaret platformu. Peki İkas sadece "tıkla-aç" bir çözüm mü, yoksa geliştiriciler için de güçlü bir backend mi?
Bu rehberde İkas'ın GraphQL API'sini kullanarak Next.js 16 App Router üzerinde tam headless bir mağaza kurulumunu ele alacağız. OAuth 2.0 token akışından SSG ile statik ürün sayfalarına, ISR ile canlı katalog güncellemelerine ve webhook entegrasyonuna kadar her adımı TypeScript ile örnekleyeceğiz.
İkas Nedir? 2026 Türkiye E-Ticaret Ekosistemindeki Yeri
İkas, Türk girişimciler ve KOBİ'ler için tasarlanmış yerli bir e-ticaret SaaS platformudur. Shopify'a en yakın Türkçe alternatif olarak konumlanmaktadır. İkas'ı rakiplerinden ayıran birkaç kritik özellik var:
- Yerleşik Trendyol ve Hepsiburada senkronizasyonu: Ayrı entegrasyon ücreti yok
- Yerli ödeme gateway'leri: Türk bankalarıyla doğrudan anlaşmalar
- GraphQL API: Geliştiriciler için tam headless mimari desteği
- EUR bazlı fiyatlandırma: Güncel planlar için ikas.com adresini ziyaret edin
2026 sonuna kadar 30.000 mağaza hedefi olan İkas, geliştiriciye yönelik pivot yapıyor: resmi API dokümantasyonu (ikas.dev), Zapier MCP entegrasyonu ve GPT bağlantısı bunun somut göstergeleri.
Headless E-Ticaret Ne Demek? Ne Zaman Tercih Edilir?
Headless mimaride backend (ürün veritabanı, sipariş yönetimi, stok) ile frontend (kullanıcı arayüzü) birbirinden tamamen ayrılır. İkas backend görevi görürken, siz Next.js ile istediğiniz frontend'i inşa edersiniz.
Headless tercih etmeli misiniz?
| Senaryo | Karar |
|---|---|
| Standart mağaza, hızlı açılış | ❌ İkas tema editörü yeterli |
| Marka kimliğine özel tasarım | ✅ Headless |
| Yüksek performans hedefi (Core Web Vitals) | ✅ Headless |
| Harici sistem entegrasyonu (CRM, ERP) | ✅ Headless |
| Çoklu kanal (web + mobil + kiosk) | ✅ Headless |
| Küçük bütçe, geliştirici yok | ❌ Tema editörü |
Proje Kurulumu
bun create next-app@latest ikas-headless --typescript --tailwind --app
cd ikas-headless
bun add graphql graphql-request
bun add -d @graphql-codegen/cli @graphql-codegen/typescript
Ortam değişkenlerini ayarlayalım:
# .env.local
IKAS_STORE_NAME="magazaadi" # myikas.com subdomain'i
IKAS_CLIENT_ID="your_client_id"
IKAS_CLIENT_SECRET="your_client_secret"
OAuth 2.0 ile Token Alma
İkas, client_credentials grant type kullanan OAuth 2.0 akışını uygular. Bir kullanıcı oturumu gerekmez; sunucudan sunucuya kimlik doğrulaması yapılır.
App Oluşturma
- İkas Dashboard → Apps → My Apps → Create Private App
- İzinleri seçin:
read_products,write_products,read_orders,write_orders,read_customers,read_inventories,write_inventories - Oluşturulan
client_idveclient_secret'i kopyalayın
Token Servisi
Token geçerlilik süresi 14.400 saniye (4 saat)'tir. Her istekte yeni token almak yerine, süre dolmadan önce yenilemek performans açısından doğru yaklaşımdır:
// lib/ikas/auth.ts
interface TokenCache {
token: string
expiresAt: number
}
let tokenCache: TokenCache | null = null
export async function getIkasToken(): Promise<string> {
const now = Date.now()
// Cache'de geçerli token varsa kullan (30 saniye erken yenile)
if (tokenCache && tokenCache.expiresAt - 30_000 > now) {
return tokenCache.token
}
const storeName = process.env.IKAS_STORE_NAME!
const response = await fetch(
`https://${storeName}.myikas.com/api/admin/oauth/token`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
grant_type: "client_credentials",
client_id: process.env.IKAS_CLIENT_ID!,
client_secret: process.env.IKAS_CLIENT_SECRET!,
}),
},
)
if (!response.ok) {
const error = await response.text()
throw new Error(`İkas token alınamadı: ${response.status} — ${error}`)
}
const data = (await response.json()) as {
access_token: string
expires_in: number
token_type: string
}
// 14.400 saniyelik geçerlilik süresi
tokenCache = {
token: data.access_token,
expiresAt: now + data.expires_in * 1000,
}
return tokenCache.token
}
GraphQL Client
İkas iki API versiyonu sunar:
- v1:
https://api.myikas.com/api/v1/admin/graphql(eski) - v2:
https://api.myikas.com/api/v2/admin/graphql(önerilen)
v2'yi kullanacağız:
// lib/ikas/client.ts
import { GraphQLClient } from "graphql-request"
import { getIkasToken } from "./auth"
const IKAS_GRAPHQL_URL = "https://api.myikas.com/api/v2/admin/graphql"
export async function getIkasClient(): Promise<GraphQLClient> {
const token = await getIkasToken()
return new GraphQLClient(IKAS_GRAPHQL_URL, {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
})
}
Temel GraphQL Query'leri
Ürün Listesi — listProduct
// lib/ikas/queries/products.ts
import { gql } from "graphql-request"
export const LIST_PRODUCTS_QUERY = gql`
query ListProducts($page: Int, $limit: Int, $categoryId: String) {
listProduct(
pagination: { page: $page, limit: $limit }
filter: { categoryId: { eq: $categoryId } }
) {
data {
id
name
slug
description
status
images {
id
fileName
originalFileName
order
}
variants {
id
sku
price {
sellPrice
buyPrice
currency
}
stock {
stockCount
}
weight
}
categories {
id
name
}
}
pagination {
totalCount
currentPage
pageSize
}
}
}
`
export interface IkasProduct {
id: string
name: string
slug: string
description: string | null
status: "ACTIVE" | "PASSIVE" | "DRAFT"
images: Array<{
id: string
fileName: string
originalFileName: string
order: number
}>
variants: Array<{
id: string
sku: string
price: {
sellPrice: number
buyPrice: number
currency: string
}
stock: { stockCount: number }
weight: number | null
}>
categories: Array<{ id: string; name: string }>
}
export interface ListProductsResponse {
listProduct: {
data: IkasProduct[]
pagination: {
totalCount: number
currentPage: number
pageSize: number
}
}
}
Sipariş Listesi — listOrder
// lib/ikas/queries/orders.ts
import { gql } from "graphql-request"
export const LIST_ORDERS_QUERY = gql`
query ListOrders($page: Int, $status: String) {
listOrder(
pagination: { page: $page, limit: 20 }
filter: { status: { eq: $status } }
sort: { createdAt: DESC }
) {
data {
id
orderNumber
status
totalPrice
currency
createdAt
customer {
id
email
firstName
lastName
}
orderLineItems {
id
quantity
price
product {
id
name
}
variant {
id
sku
}
}
shippingAddress {
addressLine1
city
country
}
}
pagination {
totalCount
currentPage
pageSize
}
}
}
`
Ürün Güncelleme — saveProduct
saveProduct mutation'ı hem oluşturma hem güncelleme işlemi yapar (upsert):
// lib/ikas/mutations/products.ts
import { gql } from "graphql-request"
export const SAVE_PRODUCT_MUTATION = gql`
mutation SaveProduct($input: SaveProductInput!) {
saveProduct(input: $input) {
id
name
slug
status
}
}
`
export interface SaveProductInput {
id?: string // varsa güncelleme, yoksa oluşturma
name: string
description?: string
status: "ACTIVE" | "PASSIVE" | "DRAFT"
variants: Array<{
id?: string
sku: string
price: {
sellPrice: number
buyPrice: number
currency: string
}
stock: { stockCount: number }
}>
categoryIds?: string[]
}
Kullanım Örneği
// lib/ikas/services/product-service.ts
import { getIkasClient } from "../client"
import {
LIST_PRODUCTS_QUERY,
type ListProductsResponse,
} from "../queries/products"
export async function fetchProducts(page = 1, limit = 20) {
const client = await getIkasClient()
const data = await client.request<ListProductsResponse>(LIST_PRODUCTS_QUERY, {
page,
limit,
})
return data.listProduct
}
export async function fetchAllProductSlugs(): Promise<string[]> {
const client = await getIkasClient()
const allSlugs: string[] = []
let page = 1
let hasMore = true
while (hasMore) {
const data = await client.request<ListProductsResponse>(
LIST_PRODUCTS_QUERY,
{ page, limit: 100 },
)
const { data: products, pagination } = data.listProduct
allSlugs.push(...products.map((p) => p.slug))
hasMore = page * pagination.pageSize < pagination.totalCount
page++
}
return allSlugs
}
SSG ile Ürün Sayfaları
Next.js 16 App Router ile tüm ürün sayfalarını build time'da statik olarak üretelim. Binlerce ürün için generateStaticParams mükemmel çalışır:
// app/urunler/[slug]/page.tsx
import { notFound } from "next/navigation"
import { fetchAllProductSlugs, fetchProductBySlug } from "@/lib/ikas/services/product-service"
import type { Metadata } from "next"
// Build time'da tüm ürün slug'larını üret
export async function generateStaticParams() {
const slugs = await fetchAllProductSlugs()
return slugs.map((slug) => ({ slug }))
}
// Dinamik metadata
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>
}): Promise<Metadata> {
const { slug } = await params
const product = await fetchProductBySlug(slug)
if (!product) return {}
return {
title: `${product.name} | Mağazamız`,
description: product.description ?? `${product.name} ürününü inceleyin.`,
openGraph: {
images: product.images[0]
? [{ url: `https://cdn.myikas.com/images/${product.images[0].fileName}` }]
: [],
},
}
}
export default async function ProductPage({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const product = await fetchProductBySlug(slug)
if (!product) notFound()
const mainVariant = product.variants[0]
const mainImage = product.images[0]
return (
<main>
<div>
{mainImage && (
<img
src={`https://cdn.myikas.com/images/${mainImage.fileName}`}
alt={product.name}
width={800}
height={800}
/>
)}
<div>
<h1>{product.name}</h1>
{product.description && <p>{product.description}</p>}
{mainVariant && (
<p>
{mainVariant.price.sellPrice.toLocaleString("tr-TR", {
style: "currency",
currency: mainVariant.price.currency,
})}
</p>
)}
</div>
</div>
</main>
)
}
Next.js 16 Notu:
paramsartıkPromise<{}>tipindedir veawaitile çözülmelidir. Sync erişim Next.js 16'da tamamen kaldırılmıştır.
ISR ile Katalog Güncellemeleri
Yüzlerce veya binlerce ürününüz varsa hepsini her build'de yeniden üretmek verimsizdir. ISR (Incremental Static Regeneration) ile sayfaları arkaplanda yeniliyoruz:
// app/urunler/[slug]/page.tsx — ISR eklemesi
// Next.js 16'da segment config
export const revalidate = 3600 // 1 saatte bir yenile
// VEYA dinamik revalidation için:
// import { revalidatePath } from "next/cache"
// Webhook geldiğinde çağrılır
Ürün sayfası listesi için:
// app/urunler/page.tsx
export const revalidate = 1800 // 30 dakikada bir
export default async function ProductsPage() {
const { data: products, pagination } = await fetchProducts(1, 24)
return (
<main>
<h1>Ürünler</h1>
<div>
{products.map((product) => (
<a key={product.id} href={`/urunler/${product.slug}`}>
{product.name}
</a>
))}
</div>
</main>
)
}
Webhook ile Anlık Güncelleme
ISR belirli aralıklarla yeniler; ancak ürün güncellemesi anında yansımasını istiyorsanız webhook kullanın.
Webhook Kaydetme
// scripts/register-webhook.ts
import { getIkasClient } from "@/lib/ikas/client"
import { gql } from "graphql-request"
const SAVE_WEBHOOK_MUTATION = gql`
mutation SaveWebhook($input: SaveWebhookInput!) {
saveWebhook(input: $input) {
id
endpoint
events
}
}
`
async function registerWebhook() {
const client = await getIkasClient()
const result = await client.request(SAVE_WEBHOOK_MUTATION, {
input: {
endpoint: "https://siteniz.com/api/webhooks/ikas",
events: ["PRODUCT_UPDATED", "PRODUCT_CREATED", "ORDER_CREATED"],
},
})
console.log("Webhook kaydedildi:", result)
}
registerWebhook()
Webhook Handler
// app/api/webhooks/ikas/route.ts
import { revalidatePath, revalidateTag } from "next/cache"
import { NextRequest, NextResponse } from "next/server"
interface IkasWebhookPayload {
event: string
data: {
id: string
slug?: string
}
}
export async function POST(request: NextRequest) {
let payload: IkasWebhookPayload
try {
payload = await request.json()
} catch {
return NextResponse.json({ error: "Geçersiz payload" }, { status: 400 })
}
const { event, data } = payload
switch (event) {
case "PRODUCT_UPDATED":
case "PRODUCT_CREATED":
// Belirli ürün sayfasını yenile
if (data.slug) {
revalidatePath(`/urunler/${data.slug}`)
}
// Ürün listesini yenile
revalidatePath("/urunler")
revalidateTag("products")
break
case "ORDER_CREATED":
// Sipariş bildirimi işle
console.log("Yeni sipariş:", data.id)
break
default:
console.log("Bilinmeyen event:", event)
}
return NextResponse.json({ received: true })
}
Trendyol ve Hepsiburada Entegrasyonu
İkas'ın Trendyol ve Hepsiburada entegrasyonu hakkında önemli bir not: bu entegrasyonlar developer API'si değil, İkas Admin Panel üzerinden çalışır.
Trendyol: İkas'tan Trendyol'a ürün push edilebilir. Stok ve fiyat senkronizasyonu otomatik yönetilir.
Hepsiburada: Hepsiburada'dan İkas'a ürün çekilebilir, ancak İkas'tan Hepsiburada'ya push yapılamaz. Bu önemli bir kısıtlamadır.
Sonuç: Trendyol/Hepsiburada senkronizasyonu için GraphQL API yazmanıza gerek yok — İkas dashboard'undaki entegrasyon ayarlarını yapılandırmanız yeterli. Sipariş akışı, stok güncellemeleri ve fiyat senkronizasyonu otomatik çalışır.
SEO: Ürün JSON-LD Şeması
Ürün sayfalarına yapılandırılmış veri ekleyerek arama motorlarında zengin sonuçlar (rich results) elde edebilirsiniz:
// components/ProductJsonLd.tsx
import type { IkasProduct } from "@/lib/ikas/queries/products"
interface ProductJsonLdProps {
product: IkasProduct
}
export function ProductJsonLd({ product }: ProductJsonLdProps) {
const mainVariant = product.variants[0]
const mainImage = product.images[0]
const jsonLd = {
"@context": "https://schema.org",
"@type": "Product",
name: product.name,
description: product.description,
image: mainImage
? `https://cdn.myikas.com/images/${mainImage.fileName}`
: undefined,
sku: mainVariant?.sku,
offers: mainVariant
? {
"@type": "Offer",
price: mainVariant.price.sellPrice,
priceCurrency: mainVariant.price.currency,
availability:
mainVariant.stock.stockCount > 0
? "https://schema.org/InStock"
: "https://schema.org/OutOfStock",
}
: undefined,
}
return (
<script
type="application/ld+json"
// biome-ignore lint/security/noDangerouslySetInnerHtml: JSON-LD şeması
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
)
}
İkas vs Shopify: Türkiye Pazarı için Teknik Karşılaştırma
Research verilerine dayanan güncel karşılaştırma:
| Kriter | İkas | Shopify |
|---|---|---|
| API tipi | GraphQL (Admin) | GraphQL + REST |
| Kimlik doğrulama | OAuth 2.0 client_credentials | OAuth 2.0 (user flow) |
| TR marketplace entegrasyonu | Yerleşik (Trendyol, Hepsiburada, n11) | 3. parti uygulama gerekli |
| TR ödeme gateway | Yerleşik banka entegrasyonları | Sınırlı |
| Fiyat para birimi | EUR | USD |
| Dokümantasyon kalitesi | İyi (v2 önerilen) | Mükemmel |
| Türk geliştirici topluluğu | Az (büyüyor) | Geniş (İngilizce) |
| Headless desteği | ✅ GraphQL API | ✅ Storefront API |
Türkiye'de İkas'ı tercih etmeniz gereken durumlar:
- Trendyol/Hepsiburada entegrasyonu öncelikliyse
- Türk ödeme yöntemleri (yerli banka sanal POS) gerekiyorsa
- Türk iş süreçleri (KDV, e-fatura) için yerli destek lazımsa
Shopify'ı tercih etmeniz gereken durumlar:
- Uluslararası satış hedefleniyorsa
- Daha zengin uygulama ekosistemi gerekiyorsa
- İngilizce kaynaklar ve büyük topluluk önemliyse
Ne Zaman İkas, Ne Zaman Custom Backend?
İkas API kullanın:
├── Türk marketplace senkronizasyonu gerekiyor
├── Ödeme altyapısı hızla kurulacak
├── Ürün/sipariş yönetimi İkas üzerinde kalacak
└── Müşteri İkas admin panelini kullanacak
Custom backend yazın:
├── Çok özel iş mantığı var (karmaşık fiyatlandırma, üyelik sistemi)
├── Mevcut ERP/CRM ile derin entegrasyon gerekiyor
├── İkas'ın veri modeliyle uyumsuzluk var
└── Tam veri sahipliği ve taşınabilirlik kritik
Production Kontrol Listesi
- Token cache mekanizması aktif (gereksiz token istekleri önlenir)
- API v2 endpoint kullanılıyor (
api.myikas.com/api/v2/admin/graphql) -
IKAS_CLIENT_SECRETsadece server-side kod ve ortam değişkenlerinde - Rate limit handling: API hata yanıtları için retry mantığı
- ISR revalidation süreleri belirlendi
- Webhook endpoint'i güvenliğe alındı (opsiyonel imza doğrulama)
-
generateStaticParamsile kritik ürün sayfaları build time'da üretiliyor - Ürün resimleri için
next/imageve uygunsizesprop kullanılıyor - Hata durumları ele alındı:
notFound(), try-catch blokları - Türkçe karakter testleri yapıldı (ürün adları, kategoriler)
- Trendyol/Hepsiburada entegrasyonu İkas dashboard'undan yapılandırıldı
Sıkça Sorulan Sorular
İkas API için ücret ödeniyor mu?
API erişimi İkas plan aboneliğinize dahildir; ayrı API ücreti alınmaz. Hangi planın API erişimini içerdiğini öğrenmek için ikas.com adresindeki güncel plan bilgilerini inceleyin.
OAuth token ne kadar süre geçerli?
expires_in değeri 14.400 saniye (4 saattir). Token cache mekanizması kullanarak her GraphQL isteğinde yeni token almaktan kaçının. Kod örneğimizde 30 saniye erken yenileme stratejisi uyguladık.
İkas GraphQL v1 mi v2 mi kullanmalıyım?
v2'yi kullanın. https://api.myikas.com/api/v2/admin/graphql adresindeki v2, daha güncel ve önerilen endpoint'tir. v1 eski projeler için destekleniyor olsa da yeni geliştirmelerde v2 başlayın.
Trendyol siparişlerini API ile çekebilir miyim?
Hayır. Trendyol/Hepsiburada entegrasyonu İkas Admin Panel üzerinden çalışır ve developer API'si sunmaz. Siparişler İkas'a otomatik aktarılır; siz listOrder query'si ile İkas üzerindeki tüm siparişlere (Trendyol kaynaklı dahil) erişebilirsiniz.
saveProduct ile mevcut ürünü güncelleyebilir miyim?
Evet. saveProduct bir upsert mutation'dır: id alanı geçerseniz güncelleme yapar, geçmezseniz yeni ürün oluşturur. Toplu güncelleme için bulkUpdateProducts mutation'ını kullanabilirsiniz.
Webhook güvenliğini nasıl sağlarım?
İkas webhook gönderdiğinde endpoint'inizi kamuya açık bırakmak zorundasınız. Güvenlik için webhook handler'ınıza bir secret key doğrulaması ekleyebilirsiniz: İkas'tan gelen isteklerin belirli bir header içermesini bekleme stratejisi uygulanabilir. Detaylar için ikas.dev dokümantasyonunu inceleyin.
ISR ve webhook'u birlikte mi kullanmalıyım?
Kullanım senaryonuza bağlı: Ürünleriniz sık değişmiyorsa ISR (1 saat revalidation) yeterlidir. Fiyat/stok anlık yansıması kritikse webhook + revalidatePath kombinasyonunu kullanın. İkisini birden kullanmak en güvenli yaklaşımdır.
Sonuç
İkas, Next.js geliştiricileri için güçlü bir headless backend alternatifi sunuyor. OAuth 2.0 ile güvenli kimlik doğrulama, kapsamlı GraphQL API'si ve yerleşik Türk marketplace entegrasyonlarıyla Türkiye pazarına yönelik e-ticaret projelerinde ciddi bir tercih nedenine sahip.
Bu rehberde ele aldığımız:
- OAuth 2.0
client_credentialsakışı ve token cache - GraphQL v2 ile
listProduct,listOrder,saveProductörnekleri - Next.js 16 App Router ile SSG ürün sayfaları
- ISR ile otomatik katalog yenileme
- Webhook ile anlık güncelleme
- Trendyol/Hepsiburada entegrasyonunun dashboard üzerinden çalıştığı bilgisi
- İkas vs Shopify teknik karşılaştırması
Headless İkas projelerinizde karmaşık bir mimari planlamanız gerekiyorsa veya mevcut bir Next.js projesine İkas entegrasyonu eklemek istiyorsanız iletişime geçin — e-ticaret altyapısı konusunda deneyimli bir geliştirici olarak yardımcı olmaktan memnuniyet duyarım.
