Açık konuşayım: WooCommerce REST API çalışır, ve çoğu projede onunla gayet mutlu olursun. Ama karmaşık, iç içe geçmiş ürün verisini tek istekte çekmesi gereken bir headless frontend kuruyorsan, REST bir noktadan sonra seni yorar — her ekran için ayrı bir uç noktaya gidip gelmek, kullanmadığın onlarca alanı boşuna taşımak. GraphQL tam burada nefes aldırır. WPGraphQL ve WooGraphQL eklentisiyle frontend’in tam olarak ihtiyacı olan veriyi ister; ne fazlası, ne eksiği.
Bu yazı bir referans niteliğinde — kuruluma, temel sorgulara, mutasyonlara ve performansa kadar sırayla iniyoruz. Kod bloklarını olduğu gibi kopyalayıp kendi mağazana uyarlayabilirsin. Ama önce şunu netleştireyim: GraphQL “daha iyi” olduğu için değil, senin işine daha çok uyduğu için tercih edilir. İkisinin de yeri var; nerede hangisine uzanacağını en sonda tabloyla bağlıyorum.
Kurulum
WordPress backend’inde iki eklenti kur:
- WPGraphQL — WordPress’e
/graphqlendpoint’i ekler - WooGraphQL — WPGraphQL’i WooCommerce türleri ve sorguları ile genişletir
GraphQL endpoint’in: https://your-store.com/graphql
WooCommerce için Neden REST Yerine GraphQL
Bir ürün listeleme sayfasını düşün. REST ile şunlara ihtiyacın var:
GET /wc/v3/products?per_page=12 → Ürünler (temel bilgi)
GET /wc/v3/products/categories → Tüm kategoriler (filtreler için)
GET /wc/v3/products/attributes → Tüm özellikler (filtreler için)
GET /wc/v3/products/{id}/variations → Varyasyonlar (ürün başına)
Bu 4+ API çağrısı, her biri ihtiyacın olmayan veriyi de yükleyerek döndürür. GraphQL ile:
query ProductListing($first: Int!, $after: String, $categoryIn: [Int]) {
products(first: $first, after: $after, where: { categoryIdIn: $categoryIn }) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
databaseId
name
slug
... on SimpleProduct {
price
regularPrice
salePrice
stockStatus
}
... on VariableProduct {
price
regularPrice
variations(first: 50) {
nodes {
databaseId
name
price
stockStatus
attributes {
nodes {
name
value
}
}
}
}
}
image {
sourceUrl(size: MEDIUM)
altText
}
productCategories {
nodes {
name
slug
}
}
}
}
}
Tek istek. Tam olarak ihtiyacın olan alanlar. Aşırı veri getirme yok. İlk kez yan yana görünce farkı hissedersin: dört gidiş-geliş yerine bir; ve döndürülen JSON’da bir tek fazladan alan yok.
Temel Sorgular
Tekil Ürün
query GetProduct($slug: ID!) {
product(id: $slug, idType: SLUG) {
databaseId
name
slug
description
shortDescription
sku
... on SimpleProduct {
price
regularPrice
salePrice
stockQuantity
stockStatus
}
... on VariableProduct {
price
regularPrice
variations(first: 100) {
nodes {
databaseId
price
regularPrice
stockStatus
stockQuantity
attributes {
nodes { name value }
}
image {
sourceUrl(size: MEDIUM_LARGE)
altText
}
}
}
defaultAttributes {
nodes { name value }
}
}
galleryImages {
nodes {
sourceUrl(size: LARGE)
altText
}
}
productCategories {
nodes { name slug }
}
attributes {
nodes {
name
options
variation
}
}
related(first: 4) {
nodes {
name
slug
... on SimpleProduct { price }
image { sourceUrl(size: MEDIUM) }
}
}
}
}
Filtreli Ürün Arama
query SearchProducts(
$search: String,
$categoryIn: [Int],
$minPrice: Float,
$maxPrice: Float,
$orderBy: ProductsOrderByEnum,
$first: Int!
) {
products(
first: $first
where: {
search: $search
categoryIdIn: $categoryIn
minPrice: $minPrice
maxPrice: $maxPrice
orderby: { field: $orderBy, order: ASC }
}
) {
nodes {
databaseId
name
slug
... on SimpleProduct {
price
regularPrice
stockStatus
}
image {
sourceUrl(size: MEDIUM)
}
}
pageInfo {
total
hasNextPage
endCursor
}
}
}
Client Kurulumu (Next.js + Apollo)
// lib/apollo-client.js
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
const client = new ApolloClient({
link: new HttpLink({
uri: process.env.NEXT_PUBLIC_GRAPHQL_URL,
credentials: 'include'
}),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
products: {
keyArgs: ['where'],
merge(existing, incoming) {
return incoming; // Replace on new query
}
}
}
}
}
})
});
export default client;
// pages/products/[slug].jsimport { gql } from '@apollo/client';
import client from '../../lib/apollo-client';
const GET_PRODUCT = gql
...; // Query from aboveexport async function getStaticProps({ params }) {
const { data } = await client.query({
query: GET_PRODUCT,
variables: { slug: params.slug }
});
return {
props: { product: data.product },
revalidate: 300 // ISR: regenerate every 5 minutes
};
}
export async function getStaticPaths() {
const { data } = await client.query({
query: gql
query { products(first: 100) { nodes { slug } } }
});
return {
paths: data.products.nodes.map(p => ({ params: { slug: p.slug } })),
fallback: 'blocking'
};
}
Mutasyonlar: Sepet ve Ödeme
WooGraphQL sepet işlemleri için mutasyonlar sağlar:
mutation AddToCart($productId: Int!, $quantity: Int!) {
addToCart(input: { productId: $productId, quantity: $quantity }) {
cartItem {
key
product { node { name } }
quantity
total
}
cart {
total
subtotal
contentsCount
}
}
}
mutation Checkout($input: CheckoutInput!) {
checkout(input: $input) {
order {
databaseId
orderNumber
status
total
}
result
}
}
Performans İpuçları
- Kalıcı sorgular: Payload boyutunu azaltmak ve rastgele sorguları önlemek için sorgularını sunucuda önceden kaydet
- Sorgu karmaşıklık limitleri: Pahalı iç içe sorguları önlemek için
graphql_max_query_depthayarla - DataLoader deseni: WPGraphQL dahili olarak DataLoader kullanır; özel resolver’ların da kullanmalı
- Fragment yeniden kullanımı: Ürün kartı fragment’leri tanımla ve sorgular arasında yeniden kullan
- Statik oluşturma: Ürün sayfaları için client-side sorgular yerine ISR ile
getStaticPropskullan
fragment ProductCard on Product {
databaseId
name
slug
... on SimpleProduct { price salePrice stockStatus }
... on VariableProduct { price }
image { sourceUrl(size: MEDIUM) altText }
}
query LatestProducts {
products(first: 8, where: { orderby: { field: DATE } }) {
nodes { ...ProductCard }
}
}
Kimlik doğrulama ve açık şema: burada dikkat
Tutorial’ların çoğunun geçiştirdiği ve benim tam da bu yüzden ayrı bir başlık açtığım kısım burası. /graphql endpoint’ini kurduğun an, WPGraphQL varsayılan olarak şemanı da introspection üzerinden herkese açar. Yani doğru yapılandırmazsan, mağazanın veri modelinin tamamı — hangi tipler var, hangi alanlar var — kamuya açık dolaşır. Salt okunur katalog verisi için bu çoğu zaman sorun değil; ama sepet, sipariş ve müşteri verisine dokunan mutasyonlar söz konusu olduğunda ayrı bir güvenlik hikâyesi başlar.
Ben olsam üç şeyi baştan hallederdim. Bir: sepet ve ödeme akışlarında kimlik/oturum yönetimini WooGraphQL’in JWT tabanlı auth’una ya da HttpOnly çerezlere yaslar, token’ı asla istemci tarafı JavaScript’in okuyabileceği bir yere koymazdım. İki: yazma yetkisi gerektiren mutasyonların gerçekten yetkilendirme kontrolünden geçtiğini test ederdim — WooGraphQL çoğunu halleder ama özel mutasyon eklediğin an sorumluluk sana geçer. Üç: üretimde introspection’ı ve graphql_max_query_depth ile derinlik limitini bilinçli olarak ayarlar, endpoint’i bir WAF veya rate limit arkasına alırdım. GraphQL’in “tek istekte her şeyi iste” gücü, kötü niyetli birinin elinde “tek istekte sunucuyu boğ” silahına dönebilir.
GraphQL vs REST: Hangisini Ne Zaman Kullanmalı
| Senaryo | Önerilen | Neden |
|---|---|---|
| Ürün listeleri (karmaşık) | GraphQL | Daha az istek, tam alanlar |
| Sepet işlemleri | REST (Store API) | Daha iyi oturum yönetimi |
| Basit CRUD | REST | Daha basit uygulama |
| Mobil uygulamalar | GraphQL | Bant genişliği verimliliği |
| Sunucu tarafı rendering | GraphQL | Sayfa başına tek istek |
| Webhook’lar | REST | GraphQL push desteklemez |
Sonuç
Toparlayayım. WPGraphQL + WooGraphQL, frontend’in karmaşık, iç içe geçmiş ürün verisine ihtiyaç duyduğunda headless WooCommerce için en temiz veri katmanı. Öğrenme eğrisi REST’ten biraz daha dik, evet — ilk gün insana fazla makine gibi gelir. Ama karşılığında aldıkların (daha az istek, tam alanla veri çekme ve tür güvenliği) üretim ölçeğinde bir headless mağaza için bu yatırımı fazlasıyla geri öder.
Benim pratik tavsiyem, ikisini rakip değil takım arkadaşı olarak görmen: okuma işlerini (ürünler, kategoriler, içerik) GraphQL ile çek, yazma ve oturum ağırlıklı işleri (sepet, ödeme, siparişler) WooCommerce Store API’nin REST tarafına bırak. “Her şeyi GraphQL’e taşıyacağım” saflığına kapılma; en dayanıklı headless mimarilerin çoğu bu ikisinin melezidir. Doğru aracı doğru işe koştuğun an, mağaza hem hızlı hem de bakması kolay olur — ki uzun vadede asıl kazandıran budur.
Last modified: Ağustos 2, 2026
United States / English
Slovensko / Slovenčina
Canada / Français
Türkiye / Türkçe