Tüm Yazılar
KategoriBackend
Okuma Süresi
23 dk
Yayın Tarihi
2026-02-13
Kelime Sayısı
2.284kelime

Kahveni hazırla - bu içerikli bir makale!

Firebase Data Connect: GraphQL + Cloud SQL

Özet

Type-safe SDK, schema-first yaklaşım, PostgreSQL backend, auto-generated queries, real-time sync ve Firebase ekosistemiyle derinlemesine entegrasyon rehberi.

  • Firebase Data Connect, Cloud SQL for PostgreSQL üzerinde çalışan schema-first GraphQL API servisi.
  • GraphQL schema'dan iOS, Android, Flutter ve Web için type-safe SDK'lar otomatik üretiliyor.
  • @table, @unique, @relation, @index direktifleriyle PostgreSQL tabloları ve ilişkiler tanımlanıyor.
  • Auth seviyeleri PUBLIC, USER_ANON, USER, USER_EMAIL_VERIFIED, NO_ACCESS olarak beş kademede sunuluyor.
Firebase Data Connect: GraphQL + Cloud SQL

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?

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 App
2 ↓ (Type-safe SDK)
3Firebase Data Connect Service
4 ↓ (GraphQL)
5Cloud SQL for PostgreSQL

Neden 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üncelle
2npm install -g firebase-tools@latest
3 
4# Data Connect başlat
5firebase init dataconnect
6 
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! @unique
6 content: String!
7 status: PostStatus! @default(value: "DRAFT")
8 author: User! @relation
9 publishedAt: Timestamp
10 createdAt: Timestamp! @default(expr: "request.time")
11}
12 
13enum PostStatus { DRAFT PUBLISHED ARCHIVED }
14 
15# connector/queries.gql — yayınlanmış yazıları listele
16query ListPosts($limit: Int! = 10) @auth(level: PUBLIC) {
17 posts(
18 where: { status: { eq: PUBLISHED } }
19 orderBy: [{ publishedAt: DESC }]
20 limit: $limit
21 ) {
22 id title slug publishedAt
23 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.gql
2 
3# Kullanıcı tablosu
4type User @table {
5 id: UUID! @default(expr: "uuidV4()")
6 email: String! @unique
7 displayName: String!
8 photoUrl: String
9 bio: String
10 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! @unique
19 content: String!
20 excerpt: String
21 status: PostStatus! @default(value: "DRAFT")
22 publishedAt: Timestamp
23 author: User! @relation
24 category: Category @relation
25 viewCount: Int! @default(value: 0)
26 createdAt: Timestamp! @default(expr: "request.time")
27}
28 
29# Kategori
30type Category @table {
31 id: UUID! @default(expr: "uuidV4()")
32 name: String! @unique
33 slug: String! @unique
34 description: String
35 posts: [Post!]! @relation # Ters ilişki
36}
37 
38# Yorum
39type Comment @table {
40 id: UUID! @default(expr: "uuidV4()")
41 content: String!
42 author: User! @relation
43 post: Post! @relation
44 parentComment: Comment @relation # Nested yorumlar
45 createdAt: Timestamp! @default(expr: "request.time")
46}
47 
48# Beğeni (many-to-many)
49type Like @table(
50 key: ["user", "post"] # Composite primary key
51) {
52 user: User! @relation
53 post: Post! @relation
54 createdAt: Timestamp! @default(expr: "request.time")
55}
56 
57# Enum
58enum PostStatus {
59 DRAFT
60 PUBLISHED
61 ARCHIVED
62}

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.gql
2 
3# Tüm yayınlanmış yazıları getir (sayfalama ile)
4query ListPublishedPosts(
5 $limit: Int! = 10,
6 $offset: Int! = 0,
7 $categorySlug: String
8) @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: $offset
17 ) {
18 id
19 title
20 slug
21 excerpt
22 publishedAt
23 viewCount
24 author {
25 displayName
26 photoUrl
27 }
28 category {
29 name
30 slug
31 }
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 id
39 title
40 slug
41 content
42 publishedAt
43 viewCount
44 author {
45 id
46 displayName
47 photoUrl
48 bio
49 }
50 category {
51 name
52 slug
53 }
54 comments(orderBy: [{ createdAt: DESC }]) {
55 id
56 content
57 createdAt
58 author {
59 displayName
60 photoUrl
61 }
62 }
63 }
64}
65 
66# Kullanıcı profili ve istatistikleri
67query GetUserProfile($userId: UUID!) @auth(level: USER) {
68 user(id: $userId) {
69 id
70 displayName
71 email
72 bio
73 photoUrl
74 posts_on_author(where: { status: { eq: PUBLISHED } }) {
75 id
76 title
77 viewCount
78 }
79 comments_on_author {
80 id
81 }
82 likes_on_user {
83 post {
84 id
85 title
86 }
87 }
88 }
89}

Mutation Tanımları

graphql
1# dataconnect/connector/mutations.gql
2 
3# Yeni yazı oluştur
4mutation 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: PostStatus
14) @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 ekle
27mutation AddComment(
28 $postId: UUID!,
29 $content: String!,
30 $parentCommentId: UUID
31) @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 toggle
41mutation 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 @auth direktifini MUTLAKA kullan. level: USER giriş 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ştur
2firebase dataconnect:sdk:generate
3 
4# Çıktı:
5# ├-- ios/ → Swift SDK
6# ├-- android/ → Kotlin SDK
7# ├-- dart/ → Flutter SDK
8# └-- web/ → TypeScript SDK

TypeScript SDK Kullanımı

typescript
1// Auto-generated — elle düzenleme YAPMA
2import {
3 listPublishedPosts,
4 getPostBySlug,
5 createPost,
6 toggleLike,
7 ListPublishedPostsData,
8} from '@firebasegen/my-connector';
9 
10// Type-safe query
11const 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 → string
19 
20// Type-safe mutation
21await createPost({
22 data: {
23 title: 'Yeni Yazı',
24 slug: 'yeni-yazi',
25 content: 'İçerik...',
26 status: 'DRAFT',
27 // author otomatik: auth.uid
28 },
29});

iOS / Swift Entegrasyonu

Swift SDK Kullanımı

swift
1import FirebaseDataConnect
2 
3// Auto-generated connector
4let connector = DataConnect.dataConnect(
5 connectorConfig: ConnectorConfig(
6 serviceId: "my-service",
7 location: "us-central1"
8 )
9)
10 
11// Type-safe query
12func loadPosts(category: String? = nil) async throws -> [Post] {
13 let result = try await ListPublishedPostsQuery
14 .ref(limit: 10, offset: 0, categorySlug: category)
15 .execute()
16 
17 return result.data.posts.map { post in
18 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 mutation
30func createNewPost(title: String, content: String) async throws {
31 try await CreatePostMutation
32 .ref(data: Post_Data(
33 title: title,
34 slug: title.slugified(),
35 content: content,
36 status: .draft
37 ))
38 .execute()
39}
40 
41// Beğeni toggle
42func toggleLike(postId: UUID) async throws {
43 try await ToggleLikeMutation
44 .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ı getir
8 Future<List<Post>> getPublishedPosts({
9 int limit = 10,
10 int offset = 0,
11 String? categorySlug,
12 }) async {
13 final result = await _connector.listPublishedPosts
14 .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.getPostBySlug
34 .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üzenleyebilir
2mutation 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 sonra
2firebase dataconnect:sql:migrate --force
3 
4# Migration SQL'ini görmek için
5firebase dataconnect:sql:diff

2. 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 status
5 publishedAt viewCount createdAt updatedAt
6 author { id displayName email photoUrl bio }
7 comments { id content author { id displayName } }
8 }
9}
10 
11# İYİ: Sadece ihtiyacın olan alanlar
12query GetPostCards @auth(level: PUBLIC) {
13 posts(
14 where: { status: { eq: PUBLISHED } }
15 limit: 10
16 ) {
17 id title slug excerpt
18 author { displayName }
19 category { name }
20 }
21}

3. Index Stratejisi

graphql
1# Schema'da index tanımla
2type Post @table @index(fields: ["status", "publishedAt"]) {
3 # ...
4}
5 
6# Composite index
7type 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

  1. İlişkisel veri? Data Connect kullan — Firestore'da JOIN yapma çabası boşa
  2. Schema-first düşün — Önce veri modelini, sonra UI'ı tasarla
  3. 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
  4. Auth direktiflerini her yere koy — Güvenlik varsayılan olmalı
  5. 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.

Etiketler

#Firebase#GraphQL#PostgreSQL#Cloud SQL#Type Safety#SDK#Backend
Muhittin Çamdalı

Muhittin Çamdalı

Lead Mobile Engineer

12+ yıllık deneyime sahip Lead Mobile Engineer. Swift, SwiftUI, Kotlin ve Flutter ile iOS, Android ve cross-platform mimarilerde uzman. Performanslı ve kullanıcı dostu mobil uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Onay e-postasındaki bağlantıyı açıp “Aboneliğimi onayla” düğmesine bastığında aboneliğin başlar. Bültende açılma/tıklama istatistikleri tutulur; dilediğin an tek tıkla ayrılabilirsin. Gizlilik

Paylaş

İlgili İçerik