Skip to content

Bölüm 10 / 11

API'yi elle kurcalamak

curl, Postman, .http dosyaları, DevTools Network.

11 dakikalık okuma

Önceki dokuz derste API'yi tasarladık, belgeledik ve kodladık. Bu ders bambaşka bir yetenek kazandırıyor: çalışmayan bir API'nin neden çalışmadığını bulmak. Bu, mülakatta doğrudan sorulmayan ama işe alındıktan sonra her gün kullanılan beceridir — ve iyi geliştiriciyi ayıran şey genellikle tam olarak budur. Bir hatayı "sunucuda mı, istemcide mi, arada mı" diye üç dakikada ayırabilmek, saatlerce tahmin yürütmekten farklıdır.

01Ne öğreneceksin

  • curl ile istek atmak ve çıktısını okumak
  • -w ile zamanlama ölçmek: DNS, bağlantı, TLS, ilk bayt
  • Depoda yaşayan .http dosyaları ve ortam değişkenleri
  • Postman/Insomnia: koleksiyon, ortam, newman ile CI
  • Koleksiyonlarda sır sızdırma tuzağı
  • DevTools Network: zamanlama, önbellek sütunu, "copy as curl"
  • CORS hatalarını doğru okumak
  • Belirtiden nedene giden teşhis sırası

02curl

Her yerde bulunur, betiklenebilir ve hiçbir şeyi senden gizlemez. Bilinmesi gereken bayraklar:

curl -v https://api.ornek.local/siparisler/42     # -v: istek (>) ve yanıt (<) satırlarını göster
curl -i ...                                       # yanıt başlıklarını gövdeyle birlikte yaz
curl -I ...                                       # yalnız başlık (HEAD isteği)
curl -s ...                                       # ilerleme çubuğunu sustur (betiklerde)
curl -o cikti.json ...                            # gövdeyi dosyaya yaz

curl -H "Authorization: Bearer $JETON" \
     -H "Accept: application/json" ...            # başlık ekle

curl -X POST https://api.ornek.local/siparisler \
     --json '{"urun":"kitap","adet":2}'           # gövde + Content-Type + Accept birlikte

curl --data-binary @istek.json \
     -H "Content-Type: application/json" ...      # gövdeyi dosyadan gönder

curl -u kullanici:parola ...                      # temel kimlik doğrulama
curl -L ...                                       # yönlendirmeleri izle (3xx)
curl -k ...                                       # sertifika doğrulamasını ATLA (yalnız yerelde)
curl --http2 ... / --http1.1 ...                  # protokol sürümünü zorla

-v çıktısında > ile başlayan satırlar senin gönderdiğin, < ile başlayanlar sunucunun döndüğü satırlardır; * ile başlayanlar curl'ün kendi notlarıdır (DNS çözümü, TLS el sıkışması, bağlantı yeniden kullanımı). 1. derste öğrendiğin mesaj yapısını ilk kez canlı gördüğün yer burasıdır.

Zamanlama ölçmek az bilinen ama teşhiste çok işe yarayan bir yetenektir:

curl -s -o /dev/null -w \
  'dns:%{time_namelookup}s  baglanti:%{time_connect}s  tls:%{time_appconnect}s  ilkbayt:%{time_starttransfer}s  toplam:%{time_total}s  kod:%{http_code}\n' \
  https://api.ornek.local/siparisler/42

Bu tek satır, "API yavaş" şikâyetini dört parçaya böler: DNS çözümü mü uzun sürüyor, TCP bağlantısı mı, TLS el sıkışması mı, yoksa sunucunun ilk baytı üretmesi mi (time_starttransfer — yani gerçek sunucu işi)? Cevap genellikle sonuncusudur ama diğer üçünü elemeden bunu söyleyemezsin. 5. hattın 11. dersinde konuştuğumuz "dış çağrının süresini ölç" kuralının elle karşılığıdır.

Windows'ta bir uyarı: PowerShell'de curl varsayılan olarak Invoke-WebRequest için bir takma addır ve bayraklar farklı çalışır. Gerçek aracı çağırmak için curl.exe yazılır.

03.http dosyaları

Elle deneme isteklerinin depoda yaşadığı biçimdir: Visual Studio ve VS Code (REST Client / yerleşik destek) bu dosyaları doğrudan çalıştırır. Avantajı, koleksiyonun koddan ayrı bir yerde değil, projenin yanında ve sürüm kontrolünde olmasıdır — yeni gelen geliştirici git clone yaptığında deneme isteklerini de almış olur.

@taban = https://localhost:7241/api/v2
@jeton = {{$dotenv JETON}}

### Sipariş getir
GET {{taban}}/siparisler/42
Accept: application/json
Authorization: Bearer {{jeton}}

### Sipariş oluştur
# @name olustur
POST {{taban}}/siparisler
Content-Type: application/json
Idempotency-Key: {{$guid}}

{
  "urun": "kitap",
  "adet": 2,
  "musteriEposta": "a@ornek.local"
}

### Az önce oluşturulanı getir — önceki yanıttan değer alınır
GET {{taban}}/siparisler/{{olustur.response.body.no}}
Authorization: Bearer {{jeton}}

### Koşullu güncelleme (1. ders: If-Match)
PUT {{taban}}/siparisler/42
If-Match: "v7"
Content-Type: application/json

{ "urun": "kitap", "adet": 5 }

Üç pratik: istekler ### ile ayrılır; değişkenler @ad = değer ile tanımlanır ve ortam dosyalarıyla (http-client.env.json) geliştirme/test/üretim için ayrı değer kümeleri tutulur; bir isteğe @name verilirse sonraki istekler onun yanıtından değer okuyabilir — zincirleme senaryolar böyle kurulur.

Kritik kural: jeton ve parola bu dosyalara yazılmaz. Ortam dosyası .gitignore'a eklenir ya da değerler ortam değişkeninden okunur. Depoya kaçmış bir jeton, geçmişten silinmesi zor bir sızıntıdır — 16. hatta git geçmişi konusunda bu tekrar karşına çıkacak.

7. dersteki OpenAPI belgesinden bu dosyaların üretilebildiğini de hatırla: sözleşme varsa, deneme isteklerini elle yazmak zorunda değilsin.

04Postman ve koleksiyonlar

Postman (ve Insomnia, Bruno) grafik arayüzlü istemcilerdir. Sağladıkları:

KavramNe işe yarar
Koleksiyonİlişkili isteklerin klasörlenmiş kümesi; ekiple paylaşılır
Ortam{{taban}}, {{jeton}} gibi değişkenlerin ortam başına değerleri
Ön/son betiklerİstekten önce jeton almak, yanıttan değer saklamak
TestlerYanıt üzerinde doğrulama (pm.test) — küçük bir sözleşme testi
Koleksiyon çalıştırıcıTüm isteklerin sırayla çalıştırılması
newmanAynı koleksiyonun komut satırından ve CI'da çalıştırılması
# CI'da duman testi olarak koleksiyonu çalıştır
newman run siparis-api.postman_collection.json \
       -e test-ortami.json --bail

Koleksiyonlar OpenAPI belgesinden içe aktarılabilir; bu, 7. dersteki "belgeden üretilen" listesinin bir maddesiydi ve elle koleksiyon bakımını ortadan kaldırır.

İki tuzak kıdemli seviyede beklenir. Birincisi sır sızdırma: koleksiyona gömülü bir jeton ya da parola, koleksiyon paylaşıldığında ya da buluta senkronlandığında sızar. Değerler her zaman ortam değişkeni olarak tutulur ve paylaşılan koleksiyona konmaz. İkincisi tek gerçek kaynağın kayması: koleksiyon elle bakıldığında bir süre sonra gerçek API'den sapar ve yanlış belge hâline gelir — bu yüzden ya belgeden üretilmeli ya da CI'da çalıştırılıp kırıldığında fark edilmelidir.

Araç seçimi pratik bir karardır: keşif ve ekiple paylaşım için Postman, tekrarlanabilirlik ve sürüm kontrolü için .http dosyaları, betikleme ve sunucuda hata ayıklama için curl. Üçü birbirinin alternatifi değil, tamamlayıcısıdır.

05Tarayıcı DevTools: Network

Sorun bir web arayüzünden geliyorsa asıl kanıt buradadır. Network sekmesinde her satır bir istektir; bilinmesi gerekenler:

  • Preserve log açılır — yoksa sayfa yenilendiğinde ya da yönlendirme olduğunda kanıt kaybolur. "Hata çıkıyor ama göremiyorum" şikâyetinin bir numaralı sebebi budur.
  • Status sütunu: gri/italik gösterilen kayıtlar önbellekten gelmiştir (from disk cache, from memory cache) ya da 304'tür — 1. derste öğrendiğin önbellek mekanizmasının canlı hâli. Bir değişikliğin "neden görünmediği" sorusunun cevabı çoğu zaman bu sütundadır.
  • Timing sekmesi: Queueing (tarayıcı bağlantı bekliyor — aynı sunucuya çok istek), Stalled, DNS, Initial connection, SSL, Waiting (TTFB) ve Content Download. TTFB uzunsa sorun sunucudadır; Content Download uzunsa yanıt büyüktür (2. dersteki sıkıştırma ve akış konusu); Queueing uzunsa istemci tarafında eşzamanlılık sorunu vardır.
  • Copy → Copy as cURL: tarayıcının attığı isteği birebir curl komutuna çevirir. Bu, "tarayıcıda çalışıyor, Postman'da çalışmıyor" sınıfı sorunları saniyeler içinde çözer — fark genellikle bir başlıkta ya da çerezdedir.
  • Filtreleme: Fetch/XHR ile yalnız API çağrıları, arama kutusunda status-code:500 ya da method:POST gibi süzgeçler.

CORS hatalarını doğru okumak ayrı bir beceridir çünkü belirti yanıltıcıdır. Tarayıcı konsolunda "blocked by CORS policy" yazar ve geliştirici sunucunun hata verdiğini sanır; oysa sunucu genellikle 200 dönmüştür — engelleyen tarayıcıdır.

Belirti:  konsolda "No 'Access-Control-Allow-Origin' header is present"
Gerçek:   sunucu yanıt verdi, ama izin başlığını göndermedi → tarayıcı yanıtı JS'e vermedi
Kanıt:    Network'te isteği bul; OPTIONS satırı var mı (ön kontrol/preflight)?
          Aynı isteği curl ile at → çalışıyorsa sorun kesinlikle CORS yapılandırmasındadır

Ön kontrol (preflight) OPTIONS isteği, özel başlık ya da PUT/DELETE gibi metotlarda tarayıcı tarafından otomatik atılır. 5. hattın 7. dersinde "CORS kimlik doğrulamadan sonraya kalırsa ön kontrol isteği kimliğe takılır ve tarayıcı asıl isteği hiç göndermez" demiştik — o cümlenin kanıtını Network sekmesinde OPTIONS satırının 401 alışında görürsün.

06Teşhis sırası

"API çalışmıyor" şikâyeti geldiğinde tahmin yürütmek yerine izlenecek sıra:

  1. İstek gerçekten gitti mi? Network'te satır var mı? Yoksa sorun sunucuda değil, istemci kodundadır (yanlış koşul, hata yutuldu).
  2. Durum kodu ne? Kod, sorunun sınıfını söyler ve 1 ile 5. derste kurduğun haritayı burada kullanırsın.
  3. Başlıklar doğru mu? Content-Type, Accept, Authorization, Origin. 415 ve 406 hataları neredeyse her zaman burada çözülür.
  4. Gövde ne diyor? ProblemDetails döndürüyorsan hataKodu ve traceId elindedir.
  5. Sunucu logunda ne var? traceId ile o isteğin tüm satırlarını bul (5. hat, 5. ders). Burada artık tahmin bitmiştir.
  6. Ara katman mı bozuyor? Aynı isteği vekili atlayarak doğrudan uygulamaya at. Sonuç değişiyorsa sorun ters vekilde ya da ağ geçidindedir (5. hat, 13. ders).
BelirtiOlası nedenİlk bakılacak
Tarayıcıda çalışıyor, Postman'da çalışmıyorÇerez ya da eksik başlıkCopy as cURL ile karşılaştır
Konsolda CORS hatasıSunucu izin başlığı göndermiyorOPTIONS satırı ve Origin
401 alıyorum ama jetonum varSüre dolmuş, yanlış hedef kitle, başlık adıJetonun içeriği ve Authorization
403 alıyorumKimlik doğru, yetki yokRol/politika (5. hat, 8. ders)
415 alıyorumContent-Type yanlış ya da eksikİstek başlıkları
Değişikliğim görünmüyorÖnbellekStatus sütunu: 304 / from cache
Bazen çalışıyor bazen çalışmıyorYük dengeleyici arkasında kopyalar farklıSürüm/kopya kimliği başlığı
Yerelde çalışıyor, sunucuda 502Uygulama ayağa kalkmıyor ya da vekil yanlışUygulama logu + vekil logu

Son olarak üretimde kurcalama kuralları: yazma isteklerini üretimde elle atmak gerçek veri değiştirir — özellikle Postman'da yanlış ortam seçiliyken. Ortam adlarını belirgin yap, üretim ortamını ayrı ve dikkat çekici adlandır, yazma isteklerini üretim koleksiyonuna hiç koyma. Ayrıca -k ile sertifika doğrulamasını atlama alışkanlığı yerelde kalmalıdır; üretimde sertifika hatası, çözülmesi gereken gerçek bir sorundur. Trafiği daha derin incelemek gerekirse mitmproxy ya da Fiddler gibi araçlar vardır; bunlar TLS'i açtıkları için yalnız kendi sistemlerinde ve izinle kullanılır.

07Mini alıştırma — Teşhis pratiği

Yaklaşık 20 dakika · 5. hatta yazdığın servis + tarayıcı

  1. Ham mesaj: curl -v ile bir GET at ve çıktıdaki *, >, < satırlarını ayır.
  2. Zamanlama: -w ile DNS, bağlantı, TLS ve ilk bayt sürelerini ölç. Toplam sürenin çoğu nerede geçti?
  3. .http dosyası: projeye istekler.http ekle; taban adresi değişken yap, oluşturma isteğine @name ver ve sonraki istekte dönen numarayı kullan.
  4. Sır disiplini: jetonu dosyaya yazmadan ortam dosyasından oku ve o dosyayı .gitignore'a ekle.
  5. 415 üret: POST isteğini Content-Type başlığı olmadan gönder. Hangi kodu aldın? Başlığı ekleyip tekrarla.
  6. Önbellek kanıtı: ETag dönen bir ucu tarayıcıda iki kez çağır ve Network'te ikinci isteğin durumunu (304 / from cache) gör.
  7. CORS: farklı bir kaynaktan (basit bir HTML sayfası) API'yi fetch ile çağır. Konsolda ne yazıyor? Network'te OPTIONS satırı var mı? Aynı isteği curl ile atınca çalışıyor mu — bu ne anlama geliyor?
  8. Copy as cURL: tarayıcının attığı bir isteği curl'e çevir ve terminalde çalıştır. Hangi başlıklar tarayıcı tarafından ekleniyormuş?
  9. Uçtan uca teşhis: servisinde bilerek bir hata bırak (yanlış bağlantı dizesi), isteği at ve yukarıdaki altı adımı sırayla uygulayarak nedeni traceId üzerinden logda bul.

08Kendini sına

curl ile hangi bilgileri elde edersin?

-v ile isteğin ve yanıtın ham satırları görülür: > gönderilen, < alınan, * curl'ün kendi notları (DNS, TLS el sıkışması, bağlantı yeniden kullanımı). -i yanıt başlıklarını gövdeyle, -I yalnız başlıkları (HEAD) getirir; -H başlık ekler, --json gövdeyle birlikte Content-Type ve Accept'i ayarlar, -L yönlendirmeleri izler, -u temel kimlik doğrulama yapar, -k sertifika doğrulamasını atlar (yalnız yerelde kullanılmalı). Teşhiste en değerlisi -w ile zamanlama ölçmektir: time_namelookup, time_connect, time_appconnect (TLS) ve time_starttransfer (ilk bayt) değerleri "API yavaş" şikâyetini dört parçaya böler; ilk bayt uzunsa sorun gerçekten sunucunun işindedir, diğerleri uzunsa ağ, DNS ya da TLS tarafındadır. PowerShell'de curl bir takma ad olduğu için gerçek aracı çağırmak üzere curl.exe yazılır.

.http dosyaları ile Postman koleksiyonlarını karşılaştır.

.http dosyaları düz metindir ve projenin yanında, sürüm kontrolünde yaşar; Visual Studio ve VS Code doğrudan çalıştırır. İstekler ### ile ayrılır, @ad = değer ile değişken tanımlanır, ortam dosyalarıyla geliştirme/test/üretim için ayrı değer kümeleri tutulur ve bir isteğe @name verilerek sonraki istekler onun yanıtından değer okuyabilir. Avantajı tekrarlanabilirlik ve kod incelemesine girmesidir. Postman/Insomnia grafik arayüzlüdür; koleksiyon, ortam, ön/son betikler, pm.test ile doğrulamalar ve koleksiyon çalıştırıcı sunar, newman ile CI'da duman testi olarak koşturulabilir ve OpenAPI belgesinden içe aktarılabilir. İki tuzağı vardır: koleksiyona gömülü jeton paylaşıldığında ya da buluta senkronlandığında sır sızar — değerler ortam değişkeninde tutulur; ve elle bakılan koleksiyon zamanla gerçek API'den saparak yanlış belgeye dönüşür — bu yüzden belgeden üretilmeli ya da CI'da çalıştırılmalıdır. Üçü birbirinin alternatifi değildir: keşif ve paylaşım için Postman, tekrarlanabilirlik için .http, betikleme ve sunucuda hata ayıklama için curl.

DevTools Network sekmesinde neye bakarsın?

Önce Preserve log açılır, yoksa yenileme ya da yönlendirmede kanıt kaybolur. Status sütununda gri/italik kayıtlar önbellekten gelmiştir (from disk cache) ya da 304'tür — "değişikliğim neden görünmüyor" sorusunun cevabı genellikle buradadır. Timing sekmesi süreyi parçalara ayırır: Queueing (aynı sunucuya çok istek), DNS, Initial connection, SSL, Waiting (TTFB) ve Content Download; TTFB uzunsa sorun sunucuda, Content Download uzunsa yanıt büyük (sıkıştırma ve akış konusu), Queueing uzunsa istemci tarafında eşzamanlılık sorunu vardır. Copy as cURL, tarayıcının attığı isteği birebir komuta çevirir ve "tarayıcıda çalışıyor, Postman'da çalışmıyor" sınıfı sorunları saniyeler içinde çözer — fark genellikle bir başlıkta ya da çerezdedir. Filtreleme için Fetch/XHR ve status-code:500 gibi süzgeçler kullanılır.

CORS hatasını nasıl okursun? "API çalışmıyor" şikâyetinde hangi sırayı izlersin?

CORS hatasında belirti yanıltıcıdır: konsolda "blocked by CORS policy" yazar ve sunucunun hata verdiği sanılır, oysa sunucu genellikle 200 dönmüştür — yanıtı JavaScript'e vermeyen tarayıcıdır, çünkü izin başlığı (Access-Control-Allow-Origin) yoktur. Kanıt Network sekmesindedir: özel başlık ya da PUT/DELETE gibi metotlarda tarayıcı otomatik bir OPTIONS ön kontrol isteği atar; o satırın durumu bakılır. Aynı istek curl ile çalışıyorsa sorun kesinlikle CORS yapılandırmasındadır. Teşhis sırası: (1) istek gerçekten gitti mi — Network'te satır yoksa sorun istemci kodundadır; (2) durum kodu ne — sorunun sınıfını söyler; (3) başlıklar doğru mu — 415 ve 406 neredeyse her zaman burada çözülür; (4) gövde ne diyor — ProblemDetails varsa hata kodu ve traceId elindedir; (5) sunucu logunda traceId ile o isteğin satırlarını bul; (6) ara katman mı bozuyor — aynı isteği vekili atlayarak doğrudan uygulamaya at, sonuç değişiyorsa sorun ters vekilde ya da ağ geçidindedir. Üretimde yazma isteklerini elle atmaktan kaçınılır ve ortam adları belirgin tutulur.

09Sınav köşesi

Yazılıda nasıl sorulur

(1) Verilen bir isteği curl komutu olarak yazınız (başlık, gövde, metot) — en sık çıkan uygulamalı soru. (2) -v, -i, -I, -L, -H, -d ne yapar. (3) .http dosyası nedir, avantajı nedir. (4) Postman koleksiyonu ve ortam kavramları; newman ne işe yarar. (5) DevTools Network'te TTFB nedir. (6) Bir isteğin önbellekten geldiği nasıl anlaşılır. (7) CORS hatası nedir, sunucu mu tarayıcı mı engelliyor. (8) Ön kontrol (preflight) isteği ne zaman atılır. (9) "401 alıyorum ama jetonum var" — olası nedenler. (10) Üretimde API kurcalarken hangi önlemler alınır.

Sözlüde nasıl anlatılır — 2 dakikalık taslak

Düzen: (1) Yaklaşım — "bir API sorununda tahmin yürütmem; belirtiden nedene giden sabit bir sıram var: istek gitti mi, kodu ne, başlıkları doğru mu, gövdesi ne diyor, logda traceId ile ne var, ara katman mı bozuyor." (2) Araçlar — "curl'ü betikleme ve sunucuda hata ayıklama için, .http dosyalarını depoda tekrarlanabilir denemeler için, Postman'ı keşif ve ekiple paylaşım için kullanırım." (3) Ölçüm — "'yavaş' şikâyetinde curl -w ile DNS, bağlantı, TLS ve ilk bayt sürelerini ayırırım; sorunun sunucuda mı ağda mı olduğunu böyle söylerim." (4) Tarayıcı — "Network'te Preserve log açar, Status sütunundan önbellekten geleni ayırır, Copy as cURL ile tarayıcı isteğini terminale taşırım." (5) Kıdem vuruşu — "CORS hatasında sunucuyu suçlamam: sunucu genelde 200 dönmüştür, engelleyen tarayıcıdır — kanıt OPTIONS ön kontrol satırıdır ve aynı istek curl ile çalışıyorsa sorun kesinlikle yapılandırmadadır. Jetonları koleksiyonlara ya da .http dosyalarına yazmam, ortam değişkeninde tutarım; üretim ortamına yazma isteği koymam."