Firebase'in en büyük eleştirisi hep aynıydı: "Firestore güçlü ama ilişkisel veri modellemesi zayıf." Google bu sorunu Firebase Data Connect ile çözdü. Cloud SQL (PostgreSQL) üzerine inşa edilmiş, GraphQL ile sorgulanan ve otomatik olarak type-safe SDK üreten bir servis. Artık Firebase'in kolaylığını PostgreSQL'in gücüyle birleştirebilirsin. Haydi, Data Connect'in mimarisini, schema-first yaklaşımını, auto-generated SDK'ları ve production'da nasıl kullanacağını derinlemesine inceleyelim.
💡 Not: Firebase Data Connect, 2024'te GA oldu. Bu yazıdaki bilgiler Şubat 2026 itibarıyla güncel. Resmi kaynak: Data Connect Docs, backend: Cloud SQL, şema dili: GraphQL Spec. GraphQL Mobile yazımızda GraphQL temellerini ele almıştık — burada Firebase'e özgü detaylara odaklanıyoruz.
İçindekiler
- Data Connect Nedir?
- Schema-First Yaklaşım
- Query ve Mutation Tanımlama
- Auto-Generated SDK'lar
- iOS / Swift Entegrasyonu
- Flutter Entegrasyonu
- Authorization ve Güvenlik
- Firestore vs Data Connect
- Production Best Practices
- Sonuç ve Öneriler
Data Connect Nedir?
Firebase Data Connect, Firebase ekosistemi içinde çalışan bir GraphQL API servisi. Arkasında Cloud SQL for PostgreSQL var. Sen schema ve query tanımlarsın, Firebase otomatik olarak iOS, Android, Flutter ve Web için type-safe SDK üretir.
Mimari Genel Bakış
text
1Mobil/Web App2 ↓ (Type-safe SDK)3Firebase Data Connect Service4 ↓ (GraphQL)5Cloud SQL for PostgreSQLNeden Data Connect?
Sorun | Firestore Çözümü | Data Connect Çözümü |
|---|---|---|
İlişkisel veri | Subcollection + denormalizasyon | Foreign key + JOIN |
Complex query | Composite index + client filtering | SQL query gücü |
Type safety | Zayıf (any benzeri) | Tam type-safe SDK |
Schema | Schema-less (esnek ama riskli) | Schema-first (güvenli) |
Transaction | Batch write (sınırlı) | ACID transaction |
Aggregate | Client-side hesaplama | SUM, AVG, COUNT server-side |
Migration | Manuel | SQL migration |
Kurulum
bash
1# Firebase CLI güncelle2npm install -g firebase-tools@latest3 4# Data Connect başlat5firebase init dataconnect6 7# Proje yapısı oluşur:8# dataconnect/9# ├-- schema/ → GraphQL schema dosyaları10# ├-- connector/ → Query ve mutation tanımları11# └-- dataconnect.yaml🔍 Pro Tip: Data Connect, mevcut Firebase projene eklenebilir. Firestore'u tamamen bırakmana gerek yok — real-time sync için Firestore, ilişkisel veriler için Data Connect kullanabilirsin.
Schema-First Yaklaşım
Data Connect'te önce veritabanı şemasını GraphQL SDL ile tanımlarsın. Firebase bunu otomatik olarak PostgreSQL tablolarına dönüştürür.
GraphQL Schema + Query Hızlı Başlangıç
graphql
1# dataconnect/schema/schema.gql — minimal blog şeması2type Post @table {3 id: UUID! @default(expr: "uuidV4()")4 title: String!5 slug: String! @unique6 content: String!7 status: PostStatus! @default(value: "DRAFT")8 author: User! @relation9 publishedAt: Timestamp10 createdAt: Timestamp! @default(expr: "request.time")11}12 13enum PostStatus { DRAFT PUBLISHED ARCHIVED }14 15# connector/queries.gql — yayınlanmış yazıları listele16query ListPosts($limit: Int! = 10) @auth(level: PUBLIC) {17 posts(18 where: { status: { eq: PUBLISHED } }19 orderBy: [{ publishedAt: DESC }]20 limit: $limit21 ) {22 id title slug publishedAt23 author { displayName }24 }25}Schema tanımlandıktan sonra firebase dataconnect:sdk:generate komutu çalıştırılır ve TypeScript, Swift, Dart için type-safe SDK'lar otomatik oluşturulur. Oluşturulan SDK dosyaları elle düzenlenmemelidir.
Schema Tanımı
graphql
1# dataconnect/schema/schema.gql2 3# Kullanıcı tablosu4type User @table {5 id: UUID! @default(expr: "uuidV4()")6 email: String! @unique7 displayName: String!8 photoUrl: String9 bio: String10 createdAt: Timestamp! @default(expr: "request.time")11 updatedAt: Timestamp! @default(expr: "request.time")12}13 14# Blog yazısı15type Post @table {16 id: UUID! @default(expr: "uuidV4()")17 title: String!18 slug: String! @unique19 content: String!20 excerpt: String21 status: PostStatus! @default(value: "DRAFT")22 publishedAt: Timestamp23 author: User! @relation24 category: Category @relation25 viewCount: Int! @default(value: 0)26 createdAt: Timestamp! @default(expr: "request.time")27}28 29# Kategori30type Category @table {31 id: UUID! @default(expr: "uuidV4()")32 name: String! @unique33 slug: String! @unique34 description: String35 posts: [Post!]! @relation # Ters ilişki36}37 38# Yorum39type Comment @table {40 id: UUID! @default(expr: "uuidV4()")41 content: String!42 author: User! @relation43 post: Post! @relation44 parentComment: Comment @relation # Nested yorumlar45 createdAt: Timestamp! @default(expr: "request.time")46}47 48# Beğeni (many-to-many)49type Like @table(50 key: ["user", "post"] # Composite primary key51) {52 user: User! @relation53 post: Post! @relation54 createdAt: Timestamp! @default(expr: "request.time")55}56 57# Enum58enum PostStatus {59 DRAFT60 PUBLISHED61 ARCHIVED62}Schema Direktifleri
Direktif | Açıklama | Örnek |
|---|---|---|
@table | PostgreSQL tablosu oluştur | type User @table |
@unique | Unique constraint | email: String! @unique |
@default | Varsayılan değer | @default(value: 0) |
@relation | Foreign key ilişkisi | author: User! @relation |
@index | Veritabanı indexi | @index(fields: ["status"]) |
@check | Constraint kontrolü | @check(expr: "rating >= 1") |
Query ve Mutation Tanımlama
Schema'dan sonra, query (okuma) ve mutation (yazma) işlemlerini tanımlarsın:
Query Tanımları
graphql
1# dataconnect/connector/queries.gql2 3# Tüm yayınlanmış yazıları getir (sayfalama ile)4query ListPublishedPosts(5 $limit: Int! = 10,6 $offset: Int! = 0,7 $categorySlug: String8) @auth(level: PUBLIC) {9 posts(10 where: {11 status: { eq: PUBLISHED },12 category: { slug: { eq: $categorySlug } }13 },14 orderBy: [{ publishedAt: DESC }],15 limit: $limit,16 offset: $offset17 ) {18 id19 title20 slug21 excerpt22 publishedAt23 viewCount24 author {25 displayName26 photoUrl27 }28 category {29 name30 slug31 }32 }33}34 35# Tek yazı detayı (slug ile)36query GetPostBySlug($slug: String!) @auth(level: PUBLIC) {37 post: posts_findMany(where: { slug: { eq: $slug }, status: { eq: PUBLISHED } }) {38 id39 title40 slug41 content42 publishedAt43 viewCount44 author {45 id46 displayName47 photoUrl48 bio49 }50 category {51 name52 slug53 }54 comments(orderBy: [{ createdAt: DESC }]) {55 id56 content57 createdAt58 author {59 displayName60 photoUrl61 }62 }63 }64}65 66# Kullanıcı profili ve istatistikleri67query GetUserProfile($userId: UUID!) @auth(level: USER) {68 user(id: $userId) {69 id70 displayName71 email72 bio73 photoUrl74 posts_on_author(where: { status: { eq: PUBLISHED } }) {75 id76 title77 viewCount78 }79 comments_on_author {80 id81 }82 likes_on_user {83 post {84 id85 title86 }87 }88 }89}Mutation Tanımları
graphql
1# dataconnect/connector/mutations.gql2 3# Yeni yazı oluştur4mutation CreatePost($data: Post_Data!) @auth(level: USER) {5 post_insert(data: $data)6}7 8# Yazı güncelle (sadece yazar)9mutation UpdatePost(10 $postId: UUID!,11 $title: String,12 $content: String,13 $status: PostStatus14) @auth(expr: "auth.uid == this.author.id") {15 post_update(16 id: $postId,17 data: {18 title: $title,19 content: $content,20 status: $status,21 updatedAt_expr: "request.time"22 }23 )24}25 26# Yorum ekle27mutation AddComment(28 $postId: UUID!,29 $content: String!,30 $parentCommentId: UUID31) @auth(level: USER) {32 comment_insert(data: {33 content: $content,34 post: { id: $postId },35 author: { id_expr: "auth.uid" },36 parentComment: { id: $parentCommentId }37 })38}39 40# Beğeni toggle41mutation ToggleLike($postId: UUID!) @auth(level: USER) {42 like_upsert(data: {43 user: { id_expr: "auth.uid" },44 post: { id: $postId }45 })46}47 48# Yazı sil (soft delete → ARCHIVED)49mutation ArchivePost($postId: UUID!) @auth(expr: "auth.uid == this.author.id") {50 post_update(51 id: $postId,52 data: { status: ARCHIVED }53 )54}🔍 Pro Tip: Mutation'larda@authdirektifini MUTLAKA kullan.level: USERgiriş yapmış herkes,expr: "auth.uid == this.author.id"sadece kaynak sahibi. Güvenlik sıfırdan düşünülmeli. iOS Security yazımızdaki prensipleri backend'e de uygula.
Auto-Generated SDK'lar
Data Connect'in en büyük avantajı: query ve mutation tanımlarından otomatik olarak iOS (Swift), Android (Kotlin), Flutter (Dart) ve Web (TypeScript) SDK'ları üretmesi.
SDK Oluşturma
bash
1# SDK'ları oluştur2firebase dataconnect:sdk:generate3 4# Çıktı:5# ├-- ios/ → Swift SDK6# ├-- android/ → Kotlin SDK7# ├-- dart/ → Flutter SDK8# └-- web/ → TypeScript SDKTypeScript SDK Kullanımı
typescript
1// Auto-generated — elle düzenleme YAPMA2import {3 listPublishedPosts,4 getPostBySlug,5 createPost,6 toggleLike,7 ListPublishedPostsData,8} from '@firebasegen/my-connector';9 10// Type-safe query11const posts: ListPublishedPostsData = await listPublishedPosts({12 limit: 10,13 offset: 0,14 categorySlug: 'typescript',15});16 17// posts.posts[0].title → string (type-safe!)18// posts.posts[0].author.displayName → string19 20// Type-safe mutation21await createPost({22 data: {23 title: 'Yeni Yazı',24 slug: 'yeni-yazi',25 content: 'İçerik...',26 status: 'DRAFT',27 // author otomatik: auth.uid28 },29});iOS / Swift Entegrasyonu
Swift SDK Kullanımı
swift
1import FirebaseDataConnect2 3// Auto-generated connector4let connector = DataConnect.dataConnect(5 connectorConfig: ConnectorConfig(6 serviceId: "my-service",7 location: "us-central1"8 )9)10 11// Type-safe query12func loadPosts(category: String? = nil) async throws -> [Post] {13 let result = try await ListPublishedPostsQuery14 .ref(limit: 10, offset: 0, categorySlug: category)15 .execute()16 17 return result.data.posts.map { post in18 Post(19 id: post.id,20 title: post.title,21 slug: post.slug,22 excerpt: post.excerpt ?? "",23 authorName: post.author.displayName,24 categoryName: post.category?.name ?? "Genel"25 )26 }27}28 29// Type-safe mutation30func createNewPost(title: String, content: String) async throws {31 try await CreatePostMutation32 .ref(data: Post_Data(33 title: title,34 slug: title.slugified(),35 content: content,36 status: .draft37 ))38 .execute()39}40 41// Beğeni toggle42func toggleLike(postId: UUID) async throws {43 try await ToggleLikeMutation44 .ref(postId: postId)45 .execute()46}Flutter Entegrasyonu
Dart SDK Kullanımı
dart
1import 'package:firebase_data_connect/firebase_data_connect.dart';2import 'package:my_connector/my_connector.dart';3 4class PostRepository {5 final _connector = MyConnector.instance;6 7 // Yayınlanmış yazıları getir8 Future<List<Post>> getPublishedPosts({9 int limit = 10,10 int offset = 0,11 String? categorySlug,12 }) async {13 final result = await _connector.listPublishedPosts14 .ref(15 limit: limit,16 offset: offset,17 categorySlug: categorySlug,18 )19 .execute();20 21 return result.data.posts.map((p) => Post(22 id: p.id,23 title: p.title,24 slug: p.slug,25 excerpt: p.excerpt ?? '',26 authorName: p.author.displayName,27 viewCount: p.viewCount,28 )).toList();29 }30 31 // Slug ile tek yazı32 Future<PostDetail?> getPostBySlug(String slug) async {33 final result = await _connector.getPostBySlug34 .ref(slug: slug)35 .execute();36 37 final posts = result.data.post;38 if (posts.isEmpty) return null;39 40 final p = posts.first;41 return PostDetail(42 id: p.id,43 title: p.title,44 content: p.content,45 author: Author(46 name: p.author.displayName,47 photo: p.author.photoUrl,48 ),49 comments: p.comments.map((c) => Comment(50 content: c.content,51 authorName: c.author.displayName,52 createdAt: c.createdAt,53 )).toList(),54 );55 }56}🔍 Pro Tip: Flutter Clean Architecture yazımızdaki repository pattern ile Data Connect SDK'yı sarmala. Böylece veri kaynağını (Firestore vs Data Connect) kolayca değiştirebilirsin. Domain layer SDK detaylarından habersiz kalır.
Authorization ve Güvenlik
Data Connect, Firebase Auth ile entegre güçlü bir authorization sistemi sunar:
Auth Seviyeleri
Seviye | Açıklama | Kullanım |
|---|---|---|
PUBLIC | Herkes erişebilir | Blog listesi, ürün kataloğu |
USER_ANON | Anonim dahil tüm auth | Rate limiting gereken yerlerde |
USER | Email doğrulanmış kullanıcı | Yorum yazma, beğeni |
USER_EMAIL_VERIFIED | Email doğrulanmış | Hassas işlemler |
NO_ACCESS | Sadece admin SDK | Server-side işlemler |
CEL Expression ile Detaylı Kontrol
graphql
1# Sadece kaynak sahibi düzenleyebilir2mutation UpdateProfile($data: User_Data!)3 @auth(expr: "auth.uid == vars.data.id") {4 user_update(data: $data)5}6 7# Admin rolü kontrolü8mutation DeletePost($postId: UUID!)9 @auth(expr: "'admin' in auth.token.roles") {10 post_delete(id: $postId)11}12 13# Zaman bazlı kontrol (son 24 saat)14mutation EditComment($commentId: UUID!, $content: String!)15 @auth(expr: "auth.uid == this.author.id && (request.time - this.createdAt) < duration('24h')") {16 comment_update(id: $commentId, data: { content: $content })17}Firestore vs Data Connect
Her iki servisi de production'da kullanan biri olarak karşılaştıralım:
Kriter | Firestore | Data Connect |
|---|---|---|
Veri Modeli | NoSQL (Document) | İlişkisel (SQL) |
Schema | Schema-less | Schema-first (GraphQL) |
Query | Collection query | SQL gücünde GraphQL |
JOIN | Yok (denormalizasyon) | Native JOIN |
Aggregation | count() (sınırlı) | SUM, AVG, COUNT, GROUP BY |
Real-time | Snapshot listener | Henüz sınırlı |
Type Safety | Zayıf | Tam (auto-generated SDK) |
Offline | Güçlü cache | Henüz yok |
Migration | Manuel | SQL migration |
Fiyat | Read/write/delete başına | Cloud SQL instance + query |
Ölçeklenme | Otomatik | Instance boyutunu ayarla |
Ne Zaman Hangisi?
- Firestore: Real-time sync, offline-first, basit veri modeli, hızlı prototipleme
- Data Connect: İlişkisel veri, karmaşık sorgular, type safety, SQL bilgisi olan ekip
Production Best Practices
1. Schema Migration
bash
1# Schema değişikliği yaptıktan sonra2firebase dataconnect:sql:migrate --force3 4# Migration SQL'ini görmek için5firebase dataconnect:sql:diff2. Query Optimizasyonu
graphql
1# KÖTÜ: Tüm alanları çek (over-fetching)2query GetPosts @auth(level: PUBLIC) {3 posts {4 id title slug content excerpt status5 publishedAt viewCount createdAt updatedAt6 author { id displayName email photoUrl bio }7 comments { id content author { id displayName } }8 }9}10 11# İYİ: Sadece ihtiyacın olan alanlar12query GetPostCards @auth(level: PUBLIC) {13 posts(14 where: { status: { eq: PUBLISHED } }15 limit: 1016 ) {17 id title slug excerpt18 author { displayName }19 category { name }20 }21}3. Index Stratejisi
graphql
1# Schema'da index tanımla2type Post @table @index(fields: ["status", "publishedAt"]) {3 # ...4}5 6# Composite index7type Comment @table @index(fields: ["post", "createdAt"]) {8 # ...9}Sonuç ve Öneriler
Firebase Data Connect, Firebase ekosisteminin en büyük eksikliğini kapatıyor: ilişkisel veri yönetimi. GraphQL schema-first yaklaşımı, auto-generated type-safe SDK'lar ve Cloud SQL'in gücü ile production uygulamaları için güçlü bir seçenek. GraphQL Mobile yazımızdaki GraphQL best practice'leri burada da geçerli. Firebase Advanced ile birleştirince Firebase ekosisteminin tam gücünü kullanırsın.
Öneriler
- İlişkisel veri? Data Connect kullan — Firestore'da JOIN yapma çabası boşa
- Schema-first düşün — Önce veri modelini, sonra UI'ı tasarla
- SDK'yı repository pattern ile sarmala — Veri kaynağı değişimine hazır ol. Swift 6 Yenilikler yazımızdaki concurrency pattern'leri ile güçlendir
- Auth direktiflerini her yere koy — Güvenlik varsayılan olmalı
- Firestore ile birlikte kullan — Real-time için Firestore, ilişkisel için Data Connect
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
Okuyucu Ödülü
Data Connect + Firestore hibrit pattern: İlişkisel verileri (kullanıcılar, siparişler, ürünler) Data Connect'te tut, real-time ihtiyaçları (chat mesajları, online durum, bildirimler) Firestore'da tut. İki servisi köprüleyen bir Cloud Function yaz — Data Connect'te sipariş oluşunca Firestore'a real-time bildirim yaz. Bu hibrit yaklaşım, her iki dünyanın en iyisini sunar.

