---
title: 'Parçalar ve Değişkenler'
source: 'https://academia.sh/tr/kurslar/api-tasarimi/parcalar-ve-degiskenler'
course: 'Web API Tasarımı'
language: tr
updated: '2026-08-17T18:06:45+00:00'
license: 'CC BY-SA 4.0'
---

# Parçalar ve Değişkenler

Parçanın yinelenen seçim kümesini tek yere toplaması, çakışan parçalardaki alanların birleştirilmesiyle düşen çözücü çağrısı ve değişkenlerin ayrı belge sayısını sabitlemesi.

Çalıştırıcı parçaları ve değişkenleri baştan beri destekliyordu ama ikisinin ne işe
yaradığı gösterilmedi. Bu ders iki soruyu ölçerek yanıtlar: parça yazmanın karşılığı nedir
ve değeri sorgu metnine gömmek yerine değişken kullanmak neyi değiştirir?

İkisinin de ortak bir yanı vardır: yanıtı değiştirmezler. Parçalı sorgu ile parçasız sorgu
aynı veriyi döndürür, değişkenli sorgu ile değeri gömülü sorgu aynı veriyi döndürür.
Kazanç başka yerdedir.

Bu dersteki ayrıştırıcı, şema, veri kaynağı ve çalıştırıcı önceki üç derste yazıldıkları
hâliyle kullanılır; yalnız çalıştırıcının seçim kümesini düzleyen bölümü değişecek.

## Parçanın Yaptığı İş

**Sorgu parçası** (fragment), bir tipe bağlanmış adlandırılmış seçim kümesidir; bu kurs
içinde kısaca parça denir. Aynı alanları birden çok yerde okuyan bir sorgu, o kümeyi bir
kez yazıp adıyla çağırır.

```js
// kosu.mjs — parcali ve parcasiz sorgulari karsilastirir
import { ayristir } from "./ayristirici.mjs";
import { calistir } from "./calistirici.mjs";
import { SEMA } from "./sema.mjs";
import { COZUCULER, sifirla } from "./veri.mjs";

// Ayni secim kumesi uc yerde tekrarlanmis hali
const PARCASIZ = `{
  acik:   odunc(id: "O-1") { id durum uye { ad } kalemler { sube kitap { baslik } } }
  kapali: odunc(id: "O-3") { id durum uye { ad } kalemler { sube kitap { baslik } } }
  digeri: odunc(id: "O-2") { id durum uye { ad } kalemler { sube kitap { baslik } } }
}`;

// Ayni sorgu, secim kumesi tek yerde
const PARCALI = `{
  acik:   odunc(id: "O-1") { ...OduncOzeti }
  kapali: odunc(id: "O-3") { ...OduncOzeti }
  digeri: odunc(id: "O-2") { ...OduncOzeti }
}
fragment OduncOzeti on Odunc { id durum uye { ad } kalemler { sube kitap { baslik } } }`;

// "durum" gibi bir alan degistirilecek olsa kac satira dokunulurdu?
const duzenlemeYeri = (metin, alan) => metin.split("\n").filter((s) => s.includes(alan)).length;

async function calistirVeSay(metin, degiskenler = {}) {
  const iz = [];
  sifirla();
  const s = await calistir(SEMA, COZUCULER, ayristir(metin), { degiskenler, izleyici: (b) => iz.push(b) });
  return { veri: JSON.stringify(s.data), cagri: iz.length };
}

const a = await calistirVeSay(PARCASIZ);
const b = await calistirVeSay(PARCALI);

console.log("belge      satır  'durum' geçen satır  çözücü çağrısı");
console.log(`parçasız   ${String(PARCASIZ.split("\n").length).padStart(5)}  ${String(duzenlemeYeri(PARCASIZ, "durum")).padStart(19)}  ${String(a.cagri).padStart(14)}`);
console.log(`parçalı    ${String(PARCALI.split("\n").length).padStart(5)}  ${String(duzenlemeYeri(PARCALI, "durum")).padStart(19)}  ${String(b.cagri).padStart(14)}`);
console.log(`\niki belgenin sonucu birebir aynı mı: ${a.veri === b.veri}`);
console.log(`sonuç: ${a.veri.slice(0, 96)}...`);
```

```
belge      satır  'durum' geçen satır  çözücü çağrısı
parçasız       5                    3              30
parçalı        6                    1              30

iki belgenin sonucu birebir aynı mı: true
sonuç: {"acik":{"id":"O-1","durum":"ACIK","uye":{"ad":"Ayse Yilmaz"},"kalemler":[{"sube":"merkez","kita...
```

Parçalı belge bir satır daha uzun ama seçim kümesi tek yerde duruyor. Alan eklemek ya da
çıkarmak gerektiğinde parçasız belgede üç satıra, parçalı belgede bir satıra dokunulur.
Fark üçe birdir ve kök alan sayısıyla birlikte büyür.

Sonuçların birebir aynı olması ve çözücü çağrısı sayısının değişmemesi, parçanın **çalışma
anında yok olduğunu** gösterir. Çalıştırıcı seçim kümesini düzlerken parçaları açar; o
noktadan sonra parçalı belgeyle parçasız belge arasında hiçbir fark kalmaz. Parça bir
yürütme kavramı değil, bir kaynak metin kavramıdır.

Bunun karşılığı, parçanın istemci tarafında bileşenle eşleşebilmesidir. Bileşen Tabanlı
Arayüz Geliştirme kursundaki bileşen ağacında her bileşen kendi veri gereksinimini bir
parça olarak bildirebilir ve üst bileşen bu parçaları birleştirerek tek bir sorgu kurar;
alt bileşen bir alan eklediğinde üst sorgu kendiliğinden büyür.

## Çakışan Alanların Birleştirilmesi

Parçalar birleştirilirken aynı alan birden çok kez seçilebilir. İki parça da `id` istiyorsa
ne olur?

Çalıştırıcının seçim kümesini düzleyen bölümü ayrı bir modüle alınır ve bu soruyu yanıtlar:
aynı yanıt anahtarına düşen alanlar tek çağrıya indirilir, alt seçimleri birleşir.

```js
// duzle.mjs — secim kumesini duzler; ayni yanit anahtarina dusen alanlari birlestirir
import { olasiTipler } from "./sema.mjs";

// Parcalari acar, somut tipe uymayan dallari atar, kalanlari sirayla dizer.
function ac(sema, belge, tipAdi, secim, birikim = []) {
  for (const s of secim) {
    if (s.tur === "alan") { birikim.push(s); continue; }
    const parca = s.tur === "parcaYayimi" ? belge.parcalar[s.ad] : s;
    const kosul = s.tur === "parcaYayimi" ? parca.tip : s.tip;
    if (kosul === tipAdi || olasiTipler(sema, kosul).includes(tipAdi)) ac(sema, belge, tipAdi, parca.secim, birikim);
  }
  return birikim;
}

// Ayni yanit anahtarina dusen alanlar tek cagriya indirilir, alt secimleri birlesir.
export function duzle(sema, belge, tipAdi, secim, birlestir = true) {
  const acik = ac(sema, belge, tipAdi, secim);
  if (!birlestir) return acik;
  const kume = new Map();
  for (const a of acik) {
    const onceki = kume.get(a.takma);
    if (!onceki) { kume.set(a.takma, { ...a, secim: a.secim ? [...a.secim] : null }); continue; }
    if (a.secim) onceki.secim = [...(onceki.secim ?? []), ...a.secim];
  }
  return [...kume.values()];
}
```

Çalıştırıcı bu modülün üzerine yeniden kurulur. Çözücü imzası, izleyici ve düzey sıralı
yürütme Çözücüler dersindeki gibidir; değişen iki şey vardır: düzleme işi dışarı alınmıştır
ve birleştirme kapatılabilir. Ölçüm bu anahtara dayanacak.

```js
// calistirici.mjs — duzleyicisi ayri modulden gelen calistirici
// Cozucu imzasi ve izleyici Cozuculer dersindeki gibidir; secim kumesini duzleyen bolum
// artik duzle.mjs'den gelir ve "birlestir" secenegiyle kapatilabilir.
import { tipCoz, alanTanimi } from "./sema.mjs";
import { duzle } from "./duzle.mjs";

const YAPRAK = new Set(["skaler", "sirali"]);

function degiskenleriCoz(islem, gelen) {
  const d = {};
  for (const b of islem.degiskenler) {
    const v = gelen[b.ad] ?? b.varsayilan;
    if (v === undefined && b.tip.endsWith("!")) throw new Error(`$${b.ad} değişkeni zorunlu`);
    d[b.ad] = v;
  }
  return d;
}

const argCoz = (argumanlar, dv) =>
  Object.fromEntries(Object.entries(argumanlar).map(([a, d]) => [a, d.tur === "degisken" ? dv[d.ad] : d.deger]));

async function secimCalistir(o, tipAdi, secim, kaynak, yol, sirali = false) {
  const alanlar = duzle(o.sema, o.belge, tipAdi, secim, o.birlestir);
  const sonuc = {};
  for (const a of alanlar) sonuc[a.takma] = null;      // anahtar sirasi sorgudan gelir
  const isle = async (a) => { sonuc[a.takma] = await alanCalistir(o, tipAdi, a, kaynak, `${yol}.${a.takma}`); };
  if (sirali) for (const a of alanlar) await isle(a);
  else await Promise.all(alanlar.map(isle));
  return sonuc;
}

async function alanCalistir(o, tipAdi, alan, kaynak, yol) {
  const tanim = alanTanimi(o.sema.tipler[tipAdi], alan.ad);
  const ozel = o.cozucular[tipAdi]?.[alan.ad];
  const cozucu = ozel ?? ((k) => k?.[alan.ad]);       // varsayilan cozucu: ayni adli alani okur
  const bilgi = { alan: alan.ad, ustTip: tipAdi, yol, varsayilan: !ozel };
  o.izleyici?.(bilgi);
  const deger = await cozucu(kaynak, argCoz(alan.argumanlar, o.dv), o.baglam, bilgi);
  return tamamla(o, tanim.tip, deger, alan.secim, yol);
}

async function tamamla(o, tipDizgi, deger, secim, yol) {
  if (deger === null || deger === undefined) return null;
  const t = tipCoz(tipDizgi);
  if (t.liste) return Promise.all(deger.map((d, i) => tamamla(o, t.ad, d, secim, `${yol}[${i}]`)));
  const tip = o.sema.tipler[t.ad];
  if (YAPRAK.has(tip.tur)) return deger;
  const somut = tip.tur === "nesne" ? t.ad : o.cozucular.__tip[t.ad](deger);
  return secimCalistir(o, somut, secim, deger, yol);
}

export async function calistir(sema, cozucular, belge,
    { islemAdi, degiskenler = {}, baglam = {}, izleyici, birlestir = true } = {}) {
  const islem = islemAdi ? belge.islemler.find((i) => i.ad === islemAdi) : belge.islemler[0];
  const o = { sema, cozucular, belge, dv: degiskenleriCoz(islem, degiskenler), baglam, izleyici, birlestir };
  const kok = islem.tur === "mutation" ? sema.mutasyon : sema.sorgu;
  return { data: await secimCalistir(o, kok, islem.secim, null, "", islem.tur === "mutation") };
}
```

```js
// cakisma.mjs — cakisan parcalarin ayni alani iki kez cozdurmesini olcer
import { ayristir } from "./ayristirici.mjs";
import { calistir } from "./calistirici.mjs";
import { SEMA } from "./sema.mjs";
import { COZUCULER } from "./veri.mjs";

const SORGU = `{
  odunc(id: "O-1") { id ...Kim ...Ne }
}
fragment Kim on Odunc { id durum uye { ad } }
fragment Ne  on Odunc { id durum kalemler { sube } }`;

for (const birlestir of [false, true]) {
  const iz = [];
  const s = await calistir(SEMA, COZUCULER, ayristir(SORGU), { izleyici: (b) => iz.push(b), birlestir });
  const kez = {};
  for (const b of iz) kez[`${b.ustTip}.${b.alan}`] = (kez[`${b.ustTip}.${b.alan}`] ?? 0) + 1;
  console.log(`birleştirme ${birlestir ? "açık " : "kapalı"}  çağrı=${String(iz.length).padStart(2)}  ` +
    Object.entries(kez).map(([a, n]) => `${a}×${n}`).join(" "));
  console.log(`  sonuç: ${JSON.stringify(s.data)}`);
}
```

```
birleştirme kapalı  çağrı=11  Sorgu.odunc×1 Odunc.id×3 Odunc.durum×2 Odunc.uye×1 Odunc.kalemler×1 Uye.ad×1 Kalem.sube×2
  sonuç: {"odunc":{"id":"O-1","durum":"ACIK","uye":{"ad":"Ayse Yilmaz"},"kalemler":[{"sube":"merkez"},{"sube":"sahil"}]}}
birleştirme açık   çağrı= 8  Sorgu.odunc×1 Odunc.id×1 Odunc.durum×1 Odunc.uye×1 Odunc.kalemler×1 Uye.ad×1 Kalem.sube×2
  sonuç: {"odunc":{"id":"O-1","durum":"ACIK","uye":{"ad":"Ayse Yilmaz"},"kalemler":[{"sube":"merkez"},{"sube":"sahil"}]}}
```

İki sonuç aynı, çağrı sayısı on birden sekize düştü. Birleştirme olmasaydı `id` alanı üç
kez, `durum` alanı iki kez çözülürdü; yanıtta yine tek kez görünürlerdi, çünkü aynı anahtara
yazılıyorlar. Yani birleştirmesiz yürütme yanlış sonuç vermez, **gereksiz iş** yapar.

Bu, parçaların bileşen başına yazılmasını güvenli kılan kuraldır. Beş bileşen kendi
parçasında `id` alanını isteyebilir; birleştirme sayesinde alan bir kez çözülür. Kural
olmasaydı parça yazmanın maliyeti kullanım sayısıyla çarpılırdı.

Birleştirmenin bir koşulu vardır: aynı anahtara düşen alanların çakışabilir olması gerekir.
Aynı ada iki farklı argümanla iki kez sorulan alan birleştirilemez; bu durumda takma ad
zorunludur ve iki ayrı anahtar oluşur.

## Değişkenler

Değişken, sorgu metnini değerden ayırır. Ayrımın sonucu, aynı ekranın her açılışında
sunucuya **aynı belgenin** gitmesidir.

```js
// degiskenler.mjs — degeri metne gomen sorgu ile degisken kullanan sorguyu karsilastirir
import { ayristir } from "./ayristirici.mjs";
import { calistir } from "./calistirici.mjs";
import { SEMA } from "./sema.mjs";
import { COZUCULER } from "./veri.mjs";

const KUCUK = ["O-1", "O-2", "O-3", "O-1", "O-2"];
// Buyuk yuk: 200 istek, 50 ayri kimlige dagilmis
const BUYUK = Array.from({ length: 200 }, (_, i) => `O-${(i % 50) + 1}`);

const GOMULU  = { metin: (id) => `{ odunc(id: "${id}") { id durum uye { ad } } }`, degisken: () => ({}) };
const DEGISKENLI = { metin: () => `query($id: ID!) { odunc(id: $id) { id durum uye { ad } } }`, degisken: (id) => ({ id }) };

async function kosu(baslik, kimlikler, bicim, calistirilsin) {
  const onbellek = new Map();      // sunucu ayristirilmis belgeyi metnine gore saklar
  let iska = 0, bayt = 0;
  for (const id of kimlikler) {
    const metin = bicim.metin(id);
    if (!onbellek.has(metin)) { iska++; onbellek.set(metin, ayristir(metin)); }
    bayt += Buffer.byteLength(metin) + Buffer.byteLength(JSON.stringify(bicim.degisken(id)));
    if (calistirilsin) await calistir(SEMA, COZUCULER, onbellek.get(metin), { degiskenler: bicim.degisken(id) });
  }
  console.log(`${baslik.padEnd(30)} istek=${String(kimlikler.length).padStart(3)}  ayrı belge=${String(onbellek.size).padStart(3)}  ` +
    `ayrıştırma=${String(iska).padStart(3)}  gönderilen bayt=${bayt}`);
}

await kosu("5 istek, değer metne gömülü", KUCUK, GOMULU, true);
await kosu("5 istek, değişkenli", KUCUK, DEGISKENLI, true);
await kosu("200 istek, değer metne gömülü", BUYUK, GOMULU, false);
await kosu("200 istek, değişkenli", BUYUK, DEGISKENLI, false);
```

```
5 istek, değer metne gömülü    istek=  5  ayrı belge=  3  ayrıştırma=  3  gönderilen bayt=230
5 istek, değişkenli            istek=  5  ayrı belge=  1  ayrıştırma=  1  gönderilen bayt=350
200 istek, değer metne gömülü  istek=200  ayrı belge= 50  ayrıştırma= 50  gönderilen bayt=9364
200 istek, değişkenli          istek=200  ayrı belge=  1  ayrıştırma=  1  gönderilen bayt=14164
```

Bayt sütunu değişkenli biçemin aleyhinedir ve öyle kalır: değişken bildirimi sorgu metnini
uzatır, değişkenler ayrıca gönderilir. Kazanç bayt sayısında değil, **ayrı belge sayısında**
görülür.

Beş istekte fark üçe birdir, iki yüz istekte elliye bir. Değeri metne gömen biçemde ayrı
belge sayısı ayrı değer sayısıyla birlikte büyür; değişkenli biçemde bir tanedir ve öyle
kalır. Bu sayı üç yerde işe yarar: sunucu ayrıştırma ve denetleme sonucunu belge başına
saklayabilir, kütüklerde hangi sorgunun ne sıklıkla çalıştığı gruplanabilir, ve belgeler
önceden kaydedilip istemci yalnız bir kimlik gönderebilir.

Bir de doğruluk gerekçesi vardır. Değeri metne gömmek, değeri sorgu diline **enjekte
etmek** demektir; tırnak içeren bir değerin kaçırılmadan gömülmesi sorgunun yapısını
değiştirebilir. Değişken kullanıldığında değer hiçbir zaman ayrıştırılmaz, ayrı bir JSON
gövdesinde taşınır ve tipi şemaya karşı denetlenir. SQL'de hazır deyimlerin çözdüğü sorunun
aynısı, aynı biçimde çözülür.

## Özet

- Parça, bir tipe bağlanmış adlandırılmış seçim kümesidir; yinelenen alanları tek yere
  toplayarak düzenleme yeri sayısını üçten bire indirir.
- Parça çalışma anında yok olur: parçalı ve parçasız belge aynı sonucu ve aynı çözücü çağrı
  sayısını üretir; parça bir kaynak metin kavramıdır.
- Aynı yanıt anahtarına düşen alanlar birleştirilir; birleştirmesiz yürütme yanlış sonuç
  vermez ama aynı alanı birden çok kez çözer.
- Birleştirme, parçaların bileşen başına yazılmasını güvenli kılar; olmasaydı parça yazmanın
  maliyeti kullanım sayısıyla çarpılırdı.
- Değişken kullanmak gönderilen baytı azaltmaz, ayrı belge sayısını sabitler: iki yüz
  istekte elli ayrı belge yerine bir.
- Sabit belge sayısı ayrıştırma sonucunun saklanmasını, kütüklerin gruplanmasını ve
  belgelerin önceden kaydedilmesini mümkün kılar; ayrıca değer sorgu diline hiç girmediği
  için yapı bozulamaz.

## Sonraki Adım

Çözücüler dersi bir sayıyı açıkta bırakmıştı: üç ödünç kaydı için üç üye çağrısı yapılmıştı,
oysa iki ayrı üye vardı. Parçalar ve değişkenler bu sayıya dokunmadı; birleştirme yalnız
**aynı yanıt anahtarındaki** çakışmaları giderir, farklı kayıtların aynı üyeyi istemesini
gidermez. Liste yüz kayda çıktığında yüz üye çağrısı yapılacak. Sonraki ders bu sorunu
adıyla koyar, aynı düzeydeki çağrıların birlikte başlatıldığı gerçeğine dayanan bir toplu
getirme katmanı yazar ve tur sayısının kaça düştüğünü ölçer.
