↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 11 — Konu 52: GraphQL (Opsiyonel)

3 dk okuma #flutter
Dizi · 52/61 Flutter Türkçe Tutorial
  1. Bölüm 1 — Konu 1: Flutter Nedir, Mimarisi (Widget → Element → RenderObject, Skia/Impeller)
  2. Bölüm 1 — Konu 2: Ortam Kurulumu (Flutter SDK, Android Studio/VS Code, Emulator/Simulator, DevTools'a İlk Bakış)
  3. Bölüm 1 — Konu 3: İlk Proje Yapısı (`pubspec.yaml`, `lib/`, Klasör Mimarisi, `flutter create` Anatomisi)
  4. Bölüm 1 — Konu 4: Hot Reload / Hot Restart Ne Yapıyor, Neden Önemli
  5. Bölüm 2 — Konu 5: "Her Şey Widget'tır" Felsefesi, Widget Ağacı
  6. Bölüm 2 — Konu 6: StatelessWidget vs StatefulWidget + `setState()` Derinlemesine (Rebuild Mekanizması)
  7. Bölüm 2 — Konu 7: Temel Layout — Container, Row, Column, Stack, Padding, Align, Center
  8. Bölüm 2 — Konu 8: Constraint Sistemi — "Constraints Go Down, Sizes Go Up, Parent Sets Position"
  9. Bölüm 2 — Konu 9: `Expanded`, `Flexible`, `Spacer`, Intrinsic Widget'lar
  10. Bölüm 3 — Konu 10: MaterialApp/CupertinoApp, Scaffold, AppBar
  11. Bölüm 3 — Konu 11: `ListView`, `GridView`, `SingleChildScrollView` (+ Builder Pattern)
  12. Bölüm 3 — Konu 12: Text, TextStyle, Icon, Image, Asset Yönetimi
  13. Bölüm 3 — Konu 13: Custom Widget Yazma Prensipleri (Composition Over Inheritance)
  14. Bölüm 3 — Konu 14: Navigator 1.0 — Push/Pop, Named Routes
  15. Bölüm 4 — Konu 15: Form, TextField/TextFormField, GlobalKey<FormState>, Validasyon
  16. Bölüm 4 — Konu 16: Theme Sistemi (ThemeData, ColorScheme)
  17. Bölüm 4 — Konu 17: Responsive & Adaptive Tasarım (MediaQuery, LayoutBuilder, OrientationBuilder, Breakpoint Stratejileri)
  18. Bölüm 5 — Konu 18: Future/Async-Await Flutter Bağlamında, FutureBuilder
  19. Bölüm 5 — Konu 19: `Stream`, `StreamBuilder`
  20. Bölüm 5 — Konu 20: HTTP İstekleri (`http` / `dio` Paketleri)
  21. Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)
  22. Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift
  23. Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı
  24. Bölüm 6 — Konu 24: Provider Paketi
  25. Bölüm 6 — Konu 25: Riverpod (Modern Yaklaşım, Provider'ın Halefi)
  26. Bölüm 6 — Konu 26: BLoC/Cubit Pattern (`flutter_bloc`)
  27. Bölüm 6 — Konu 27: GetX (Tartışmalı Ama Yaygın)
  28. Bölüm 6 — Konu 28: Karşılaştırma — Hangi Projede Hangisi?
  29. Bölüm 7 — Konu 29: Navigator 2.0 (Router, RouteInformationParser, RouterDelegate)
  30. Bölüm 7 — Konu 30: `go_router` Paketi (Pratik ve Modern Çözüm)
  31. Bölüm 7 — Konu 31: Deep Linking
  32. Bölüm 7 — Konu 32: Repository Pattern, Katmanlı Mimari (Data/Domain/Presentation)
  33. Bölüm 7 — Konu 33: Dependency Injection (`get_it`, `injectable`)
  34. Bölüm 7 — Konu 34: Clean Architecture Uyarlaması, SOLID Prensipleri
  35. Bölüm 8 — Konu 35: Implicit Animasyonlar
  36. Bölüm 8 — Konu 36: Explicit Animasyonlar (`AnimationController`, `Tween`, `Curve`)
  37. Bölüm 8 — Konu 37: Hero Animasyonları
  38. Bölüm 8 — Konu 38: `CustomPainter` / `Canvas`
  39. Bölüm 8 — Konu 39: Rive / Lottie Entegrasyonu
  40. Bölüm 9 — Konu 40: Platform Channels
  41. Bölüm 9 — Konu 41: Permission Yönetimi (`permission_handler`)
  42. Bölüm 9 — Konu 42: Kamera, Konum, Sensörler
  43. Bölüm 9 — Konu 43: Push Notification (Firebase Cloud Messaging)
  44. Bölüm 9 — Konu 44: Android/iOS Build Sistemleri
  45. Bölüm 10 — Konu 45: Test Yazımı (Unit, Widget, Integration, Golden)
  46. Bölüm 10 — Konu 46: CI/CD
  47. Bölüm 10 — Konu 47: Rebuild Optimizasyonu (`const`, `key` Kullanımı)
  48. Bölüm 10 — Konu 48: DevTools Profiling
  49. Bölüm 10 — Konu 49: Lazy Loading, Pagination, Büyük Liste Performansı
  50. Bölüm 11 — Konu 50: Firebase Ekosistemi (Auth, Firestore, Storage, Functions)
  51. Bölüm 11 — Konu 51: Supabase Alternatifi
  52. Bölüm 11 — Konu 52: GraphQL (Opsiyonel)
  53. Bölüm 11 — Konu 53: Store Yayınlama Süreci (İmzalama, Listing, Versiyonlama)
  54. Bölüm 11 — Konu 54: App Size, Obfuscation, Flavor Yönetimi
  55. Bölüm 12 — Konu 55: Flutter Web / Desktop
  56. Bölüm 12 — Konu 56: Custom `RenderObject` Yazımı
  57. Bölüm 12 — Konu 57: Engine Mimarisi Derinlemesine (Impeller vs Skia)
  58. Bölüm 12 — Konu 58: Plugin Geliştirme, pub.dev'e Paket Yayınlama
  59. Bölüm 12 — Konu 59: Monorepo Mimarisi (Melos)
  60. Bölüm 12 — Konu 60: Erişilebilirlik (Accessibility) Derinlemesine
  61. Bölüm 12 — Konu 61: Yerelleştirme (Localization / i18n)
Dizinin sayfası →
İçindekiler 11 başlık
  1. REST'in Sorunu — Over-fetching ve Under-fetching
  2. GraphQL'in Çözümü — "Ne İstediğini Sen Söyle"
  3. Şema ve Tip Sistemi — Dart'ın Statik Tiplemesine Benzer Bir Güvence
  4. Flutter'da Kullanım — graphql_flutter
  5. Query — Veri Okuma
  6. Mutation — Veri Değiştirme
  7. Subscription — Gerçek Zamanlı Veri
  8. GraphQL vs REST vs Firestore/Supabase — Nerede Durur?
  9. Pratik Gerçek — Ne Zaman Karşına Çıkar?
  10. 🎯 Bu Dersten Çıkarılması Gerekenler
  11. 📝 Ödevler (Opsiyonel)

Bu konu opsiyonel işaretli — GraphQL, her projede karşına çıkmaz, ama özellikle büyük/kurumsal API'lerde (GitHub API, Shopify, birçok modern SaaS ürünü) giderek yaygınlaşan bir alternatif. Temel fikri anlaman, bir gün "bu API GraphQL kullanıyor" ile karşılaştığında şaşırmaman için yeterli.

REST'in Sorunu — Over-fetching ve Under-fetching

Bölüm 5 Konu 20'de öğrendiğimiz REST API'leri hatırla — her endpoint, sabit bir yapıda veri döner:

metin
GET /kullanicilar/123
→ { id, isim, eposta, adres, telefon, kayitTarihi, ... TÜM ALANLAR }

Diyelim ki arayüzünde sadece kullanıcının ismini göstermen gerekiyor — REST, yine de tüm alanları gönderir (over-fetching — ihtiyacından fazlasını çekme). Ya da tam tersi: kullanıcının hem bilgilerini hem son 5 siparişini göstermen gerekiyorsa, iki ayrı istek atman gerekebilir (under-fetching — bir istekte yetmemesi).

GraphQL'in Çözümü — "Ne İstediğini Sen Söyle"

graphql
query {
  kullanici(id: "123") {
    isim
    siparisler(limit: 5) {
      urunAdi
      fiyat
    }
  }
}

GraphQL'de, istemci (senin Flutter uygulaman), tam olarak hangi alanları istediğini sorgunun içinde belirtir — sunucu, sadece istenen alanları, tek bir istekte döner. Bu, Bölüm 11 Konu 51'de Supabase'in .select('isim') ile "sadece bu sütunu getir" demesine kavramsal olarak benziyor — ama GraphQL bunu iç içe geçmiş ilişkiler için de (yukarıdaki örnekte olduğu gibi, kullanıcı + siparişleri tek seferde) doğal olarak destekler.

Şema ve Tip Sistemi — Dart'ın Statik Tiplemesine Benzer Bir Güvence

graphql
type Kullanici {
  id: ID!
  isim: String!
  eposta: String!
  siparisler: [Siparis!]!
}

GraphQL API'leri, Dart Bölüm 1-2'de öğrendiğimiz statik tip sistemine benzer bir şema ile tanımlanır — ! işareti, Dart'ın non-nullable (String vs String?) mantığına birebir karşılık gelir ("bu alan asla null olamaz" demektir). Bu şema sayesinde, bir GraphQL sorgusu derleme zamanında bile (uygun araçlarla) doğrulanabilir — "böyle bir alan yok" hatası, API'ye istek atmadan önce yakalanabilir.

Flutter'da Kullanım — graphql_flutter

bash
flutter pub add graphql_flutter
dart
final HttpLink httpLink = HttpLink('https://api.ornek.com/graphql');
final ValueNotifier<GraphQLClient> client = ValueNotifier(
  GraphQLClient(link: httpLink, cache: GraphQLCache()),
);

Query — Veri Okuma

dart
const String kullaniciSorgusu = r'''
  query KullaniciGetir($id: ID!) {
    kullanici(id: $id) {
      isim
      eposta
    }
  }
''';

Query(
  options: QueryOptions(
    document: gql(kullaniciSorgusu),
    variables: {'id': '123'},
  ),
  builder: (QueryResult sonuc, {refetch, fetchMore}) {
    if (sonuc.isLoading) return const CircularProgressIndicator();
    if (sonuc.hasException) return Text('Hata: ${sonuc.exception}');

    final kullanici = sonuc.data!['kullanici'];
    return Text(kullanici['isim']);
  },
)

Query widget'ı, Bölüm 5 Konu 18'de öğrendiğimiz FutureBuilder'a kavramsal olarak çok benziyor — isLoading/hasException/data durumlarını, Flutter'ın builder pattern'i (Bölüm 3 Konu 11) ile tutarlı bir şekilde yönetiyor. $id: ID! gibi değişkenler, Dart Bölüm 6'da öğrendiğimiz parametreli fonksiyon çağrısına benzer bir mantıkla, sorguyu yeniden kullanılabilir yapıyor (variables: {'id': '123'} ile "doldurulur").

Mutation — Veri Değiştirme

dart
const String kullaniciGuncelle = r'''
  mutation KullaniciGuncelle($id: ID!, $isim: String!) {
    kullaniciGuncelle(id: $id, isim: $isim) {
      id
      isim
    }
  }
''';

Mutation(
  options: MutationOptions(document: gql(kullaniciGuncelle)),
  builder: (RunMutation calistir, QueryResult? sonuc) {
    return ElevatedButton(
      onPressed: () => calistir({'id': '123', 'isim': 'Yeni İsim'}),
      child: const Text('Güncelle'),
    );
  },
)

GraphQL'de okuma işlemleri query, yazma/değiştirme işlemleri mutation olarak adlandırılır — bu, REST'teki GET (okuma) ile POST/PUT/DELETE (değiştirme) ayrımının GraphQL karşılığıdır.

Subscription — Gerçek Zamanlı Veri

dart
const String yeniMesajAkisi = r'''
  subscription {
    yeniMesaj {
      gonderen
      icerik
    }
  }
''';

Subscription(
  options: SubscriptionOptions(document: gql(yeniMesajAkisi)),
  builder: (QueryResult sonuc) {
    if (!sonuc.hasException && sonuc.data != null) {
      return Text(sonuc.data!['yeniMesaj']['icerik']);
    }
    return const Text('Mesaj bekleniyor...');
  },
)

subscription, Bölüm 11 Konu 50-51'de Firestore'un .snapshots()'ı ve Supabase'in .stream()'i ile aynı fikri — sunucudan sürekli akan veri — GraphQL dünyasında ifade eder. Altyapıda genelde WebSocket kullanılır (Bölüm 5 Konu 19'da öğrendiğimiz Stream kavramının, ağ üzerinden sürekli açık bir bağlantı ile taşınan hali).

GraphQL vs REST vs Firestore/Supabase — Nerede Durur?

REST GraphQL Firestore/Supabase
İstek başına veri kontrolü Sabit (sunucu belirler) İstemci belirler (esnek) Sorgu ile kısmen belirlenir
Birden fazla kaynak, tek istek Genelde hayır Evet (iç içe sorgular) Supabase'de join ile kısmen
Öğrenme eğrisi Düşük Orta (şema, sorgu dili) Düşük-orta
Gerçek zamanlı destek Yok (elle WebSocket kurulur) subscription ile yerleşik .snapshots()/.stream() ile yerleşik
En uygun olduğu senaryo Basit, öngörülebilir API'ler Karmaşık, iç içe veri ihtiyaçları olan büyük uygulamalar Kendi backend'ini Firebase/Supabase'e emanet eden projeler

Pratik Gerçek — Ne Zaman Karşına Çıkar?

Kendi backend'ini (Bölüm 11 Konu 50-51'deki Firebase/Supabase ya da kendi yazdığın bir sunucu) sen tasarlıyorsan, GraphQL'i baştan seçmen nadiren gerekir — REST (Bölüm 5 Konu 20) ya da Firestore/Supabase'in kendi sorgu API'leri genelde yeterlidir. GraphQL'in asıl değeri, büyük, birden fazla ekibin aynı API'yi farklı ihtiyaçlarla kullandığı kurumsal ortamlarda ortaya çıkar — ya da hazır bir GraphQL API'sine (örneğin bir üçüncü parti servis) client olarak bağlanman gerektiğinde.


🎯 Bu Dersten Çıkarılması Gerekenler

  • GraphQL, REST'in over-fetching/under-fetching sorununu, istemcinin tam olarak hangi alanları istediğini sorgunun içinde belirtmesine izin vererek çözer.
  • Şema ve tip sistemi (! ile non-nullable), Dart'ın statik tiplemesine benzer bir güvence sağlar.
  • query (okuma), mutation (yazma), subscription (gerçek zamanlı akış) — REST'teki HTTP metodlarının ve Firestore/Supabase'in stream API'lerinin GraphQL karşılıkları.
  • graphql_flutter'daki Query/Mutation widget'ları, FutureBuilder'a kavramsal olarak çok benzer bir builder pattern kullanır.
  • Pratikte, kendi backend'ini tasarlarken GraphQL zorunlu değildir — büyük/kurumsal API'lerde ya da hazır bir GraphQL servisine bağlanırken karşına çıkar.

📝 Ödevler (Opsiyonel)

  • [ ] Herkese açık bir GraphQL API'sini (örn. countries.trevorblades.com) graphql_flutter ile sorgula, sonucu bir listede göster.
  • [ ] Aynı veriyi REST ile çekmiş olsaydın kaç istek gerekeceğini, GraphQL'in tek istekte nasıl çözdüğünü karşılaştırarak kendi cümlelerinle yaz.
  • [ ] Bir GraphQL şemasındaki ! işaretlerinin, Dart'ın nullable/non-nullable (String vs String?) sistemiyle nasıl paralel olduğunu açıkla.

Sıradaki konu: Bölüm 11 — Konu 53: Store Yayınlama Süreci (İmzalama, Listing, Versiyonlama)