🇬🇧 English: Read this in English →

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 /graphql endpoint’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].js

import { gql } from '@apollo/client';

import client from '../../lib/apollo-client';

const GET_PRODUCT = gql...; // Query from above

export 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_depth ayarla
  • 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 getStaticProps kullan

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.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Close Search Window